mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
re-implement java global inspection graph using uast
This commit is contained in:
+507
-319
@@ -1,274 +1,419 @@
|
||||
/*
|
||||
* Copyright 2000-2014 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.
|
||||
*/
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
|
||||
package com.intellij.codeInspection.reference;
|
||||
|
||||
import com.intellij.codeInspection.InspectionsBundle;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.util.MethodSignatureUtil;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.intellij.util.VisibilityUtil;
|
||||
import com.siyeh.ig.psiutils.ExpressionUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.uast.*;
|
||||
import org.jetbrains.uast.visitor.AbstractUastVisitor;
|
||||
|
||||
public class RefJavaUtilImpl extends RefJavaUtil{
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class RefJavaUtilImpl extends RefJavaUtil {
|
||||
private static final Logger LOG = Logger.getInstance(RefJavaUtilImpl.class);
|
||||
|
||||
@Override
|
||||
public void addReferences(@NotNull final PsiModifierListOwner psiFrom, @NotNull final RefJavaElement ref, @Nullable final PsiElement findIn) {
|
||||
public void addReferences(@NotNull PsiModifierListOwner psiFrom, @NotNull RefJavaElement ref, @Nullable PsiElement findIn) {
|
||||
UDeclaration decl = UastContextKt.toUElement(psiFrom, UDeclaration.class);
|
||||
UElement uFindIn = UastContextKt.toUElement(findIn);
|
||||
if (decl != null && findIn != null) {
|
||||
addReferencesTo(decl, ref, new UElement[]{uFindIn});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addReferencesTo(@NotNull final UDeclaration decl, @NotNull final RefJavaElement ref, @Nullable final UElement[] findIn) {
|
||||
final RefJavaElementImpl refFrom = (RefJavaElementImpl)ref;
|
||||
if (findIn == null) {
|
||||
return;
|
||||
}
|
||||
findIn.accept(
|
||||
new JavaRecursiveElementWalkingVisitor() {
|
||||
@Override public void visitReferenceElement(PsiJavaCodeReferenceElement reference) {
|
||||
visitElement(reference);
|
||||
final PsiElement target = reference.resolve();
|
||||
for (UElement element : findIn) {
|
||||
if (element == null) continue;
|
||||
element.accept(new AbstractUastVisitor() {
|
||||
@Override
|
||||
public boolean visitAnnotation(@NotNull UAnnotation node) {
|
||||
PsiClass javaClass = node.resolve();
|
||||
if (javaClass != null) {
|
||||
final RefClassImpl refClass = (RefClassImpl)refFrom.getRefManager().getReference(javaClass.getOriginalElement());
|
||||
refFrom.addReference(refClass, javaClass.getOriginalElement(), decl, false, true, null);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (target instanceof PsiClass) {
|
||||
final PsiClass aClass = (PsiClass)target;
|
||||
final RefClassImpl refClass = (RefClassImpl)refFrom.getRefManager().getReference(aClass);
|
||||
refFrom.addReference(refClass, aClass, psiFrom, false, true, null);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public boolean visitTypeReferenceExpression(@NotNull UTypeReferenceExpression node) {
|
||||
PsiType type = node.getType();
|
||||
visitTypeRefs(type);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLiteralExpression(PsiLiteralExpression expression) {
|
||||
for (PsiReference reference : expression.getReferences()) {
|
||||
PsiElement resolve = reference.resolve();
|
||||
if (resolve instanceof PsiMember) {
|
||||
final RefElement refResolved = refFrom.getRefManager().getReference(resolve);
|
||||
refFrom.addReference(refResolved, resolve, psiFrom, false, true, null);
|
||||
if (refResolved instanceof RefMethod) {
|
||||
updateRefMethod(resolve, refResolved, expression, psiFrom, refFrom);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
private void visitTypeRefs(PsiType type) {
|
||||
type = type.getDeepComponentType();
|
||||
if (type instanceof PsiClassType) {
|
||||
type.accept(new PsiTypeVisitor<Void>() {
|
||||
@Nullable
|
||||
@Override
|
||||
public Void visitClassType(PsiClassType classType) {
|
||||
for (PsiType parameter : classType.getParameters()) {
|
||||
parameter.accept(this);
|
||||
}
|
||||
UClass target = UastContextKt.toUElement(classType.resolve(), UClass.class);
|
||||
if (target != null) {
|
||||
final RefClassImpl refClass = (RefClassImpl)refFrom.getRefManager().getReference(target.getSourcePsi());
|
||||
refFrom.addReference(refClass, target.getSourcePsi(), decl, false, true, null);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void visitReferenceExpression(PsiReferenceExpression expression) {
|
||||
visitElement(expression);
|
||||
@Override
|
||||
public boolean visitSimpleNameReferenceExpression(@NotNull USimpleNameReferenceExpression node) {
|
||||
final PsiElement target = node.resolve();
|
||||
|
||||
final JavaResolveResult result = expression.advancedResolve(false);
|
||||
final PsiElement psiResolved = result.getElement();
|
||||
visitReferenceExpression(node);
|
||||
if (target instanceof PsiClass) {
|
||||
final PsiClass aClass = (PsiClass)target;
|
||||
final RefClassImpl refClass = (RefClassImpl)refFrom.getRefManager().getReference(aClass);
|
||||
refFrom.addReference(refClass, aClass, decl, false, true, null);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
RefElement refResolved = refFrom.getRefManager().getReference(psiResolved);
|
||||
refFrom.addReference(
|
||||
refResolved, psiResolved, psiFrom, PsiUtil.isAccessedForWriting(expression),
|
||||
PsiUtil.isAccessedForReading(expression), expression
|
||||
);
|
||||
@Override
|
||||
public boolean visitLiteralExpression(@NotNull ULiteralExpression node) {
|
||||
PsiElement sourcePsi = node.getSourcePsi();
|
||||
if (sourcePsi != null) {
|
||||
for (PsiReference reference : sourcePsi.getReferences()) {
|
||||
PsiElement resolve = reference.resolve();
|
||||
if (resolve instanceof PsiMember) {
|
||||
final RefElement refResolved = refFrom.getRefManager().getReference(resolve);
|
||||
refFrom.addReference(refResolved, resolve, decl, false, true, null);
|
||||
if (refResolved instanceof RefMethod) {
|
||||
updateRefMethod(resolve, refResolved, node, decl, refFrom);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (refResolved instanceof RefMethod) {
|
||||
updateRefMethod(psiResolved, refResolved, expression, psiFrom, refFrom);
|
||||
}
|
||||
|
||||
if (psiResolved instanceof PsiMember && result.getCurrentFileResolveScope() instanceof PsiImportStaticStatement) {
|
||||
final PsiClass containingClass = ((PsiMember)psiResolved).getContainingClass();
|
||||
if (containingClass != null) {
|
||||
RefElement refContainingClass = refFrom.getRefManager().getReference(containingClass);
|
||||
if (refContainingClass != null) {
|
||||
refFrom.addReference(refContainingClass, containingClass, psiFrom, false, true, expression);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public boolean visitPrefixExpression(@NotNull UPrefixExpression node) {
|
||||
visitReferenceExpression(node);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visitPostfixExpression(@NotNull UPostfixExpression node) {
|
||||
visitReferenceExpression(node);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visitUnaryExpression(@NotNull UUnaryExpression node) {
|
||||
visitReferenceExpression(node);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visitBinaryExpression(@NotNull UBinaryExpression node) {
|
||||
visitReferenceExpression(node);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visitQualifiedReferenceExpression(@NotNull UQualifiedReferenceExpression node) {
|
||||
visitReferenceExpression(node);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visitCallableReferenceExpression(@NotNull UCallableReferenceExpression node) {
|
||||
visitReferenceExpression(node);
|
||||
processFunctionalExpression(node, getFunctionalInterfaceType(node));
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visitObjectLiteralExpression(@NotNull UObjectLiteralExpression node) {
|
||||
visitReferenceExpression(node);
|
||||
visitClass(node.getDeclaration());
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visitCallExpression(@NotNull UCallExpression node) {
|
||||
visitReferenceExpression(node);
|
||||
if (node instanceof UObjectLiteralExpression) {
|
||||
visitClass(((UObjectLiteralExpression)node).getDeclaration());
|
||||
}
|
||||
if (node.getKind() == UastCallKind.CONSTRUCTOR_CALL) {
|
||||
PsiMethod resolvedMethod = node.resolve();
|
||||
final List<UExpression> argumentList = node.getValueArguments();
|
||||
RefMethod refConstructor = processNewLikeConstruct(resolvedMethod, argumentList);
|
||||
|
||||
if (refConstructor == null) { // No explicit constructor referenced. Should use default one.
|
||||
UReferenceExpression reference = node.getClassReference();
|
||||
if (reference != null) {
|
||||
PsiElement constructorClass = reference.resolve();
|
||||
if (constructorClass instanceof PsiClass) {
|
||||
processClassReference((PsiClass)constructorClass, refFrom, decl, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
node.getTypeArguments().forEach(this::visitTypeRefs);
|
||||
}
|
||||
catch (UnsupportedOperationException e) {
|
||||
//TODO happens somewhere in kotlin plugin. Please assign those exception for Dmitry Batkovich
|
||||
LOG.error(e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void visitReferenceExpression(UExpression node) {
|
||||
PsiElement psiResolved = null;
|
||||
if (node instanceof UResolvable) {
|
||||
psiResolved = ((UResolvable)node).resolve();
|
||||
}
|
||||
else if (node instanceof UBinaryExpression) {
|
||||
psiResolved = ((UBinaryExpression)node).resolveOperator();
|
||||
}
|
||||
else if (node instanceof UUnaryExpression) {
|
||||
psiResolved = ((UUnaryExpression)node).resolveOperator();
|
||||
}
|
||||
if (psiResolved == null) {
|
||||
psiResolved = tryFindKotlinParameter(node, decl);
|
||||
}
|
||||
RefElement refResolved = refFrom.getRefManager().getReference(psiResolved);
|
||||
boolean writing = isAccessedForWriting(node);
|
||||
boolean reading = isAccessedForReading(node);
|
||||
refFrom.addReference(refResolved, psiResolved, decl, writing, reading, node);
|
||||
|
||||
if (refResolved instanceof RefMethod) {
|
||||
updateRefMethod(psiResolved, refResolved, node, decl, refFrom);
|
||||
}
|
||||
|
||||
if (psiResolved instanceof PsiMember) {
|
||||
//TODO support kotlin
|
||||
addClassReferenceForStaticImport(node, (PsiMember)psiResolved, refFrom, decl);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visitLambdaExpression(@NotNull ULambdaExpression node) {
|
||||
processFunctionalExpression(node, node.getFunctionalInterfaceType());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override public void visitEnumConstant(PsiEnumConstant enumConstant) {
|
||||
super.visitEnumConstant(enumConstant);
|
||||
processNewLikeConstruct(enumConstant.resolveConstructor(), enumConstant.getArgumentList());
|
||||
}
|
||||
private void processFunctionalExpression(UExpression expression, PsiType type) {
|
||||
PsiElement aClass = PsiUtil.resolveClassInType(type);
|
||||
if (aClass != null) {
|
||||
aClass = ((PsiClass)aClass).getSourceElement();
|
||||
}
|
||||
if (aClass != null) {
|
||||
refFrom.addReference(refFrom.getRefManager().getReference(aClass), aClass, decl, false, true, null);
|
||||
final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(type);
|
||||
if (interfaceMethod != null) {
|
||||
refFrom.addReference(refFrom.getRefManager().getReference(interfaceMethod), interfaceMethod, decl, false, true, null);
|
||||
refFrom.getRefManager().fireNodeMarkedReferenced(interfaceMethod, expression.getSourcePsi());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void visitNewExpression(PsiNewExpression newExpr) {
|
||||
super.visitNewExpression(newExpr);
|
||||
PsiMethod psiConstructor = newExpr.resolveConstructor();
|
||||
final PsiExpressionList argumentList = newExpr.getArgumentList();
|
||||
@Nullable
|
||||
private RefMethod processNewLikeConstruct(final PsiMethod javaConstructor, final List<UExpression> argumentList) {
|
||||
if (javaConstructor == null) return null;
|
||||
RefMethodImpl refConstructor = (RefMethodImpl)refFrom.getRefManager().getReference(javaConstructor.getOriginalElement());
|
||||
refFrom.addReference(refConstructor, javaConstructor, decl, false, true, null);
|
||||
|
||||
RefMethod refConstructor = processNewLikeConstruct(psiConstructor, argumentList);
|
||||
for (UExpression arg : argumentList) {
|
||||
arg.accept(this);
|
||||
}
|
||||
|
||||
if (refConstructor == null) { // No explicit constructor referenced. Should use default one.
|
||||
PsiType newType = newExpr.getType();
|
||||
if (newType instanceof PsiClassType) {
|
||||
processClassReference(PsiUtil.resolveClassInType(newType), refFrom, psiFrom, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (refConstructor != null) {
|
||||
refConstructor.updateParameterValues(argumentList, javaConstructor);
|
||||
}
|
||||
return refConstructor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLambdaExpression(PsiLambdaExpression expression) {
|
||||
super.visitLambdaExpression(expression);
|
||||
processFunctionalExpression(expression);
|
||||
}
|
||||
@Override
|
||||
public boolean visitClass(@NotNull UClass uClass) {
|
||||
RefClassImpl refClass = (RefClassImpl)refFrom.getRefManager().getReference(uClass.getSourcePsi());
|
||||
refFrom.addReference(refClass, uClass.getSourcePsi(), decl, false, true, null);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMethodReferenceExpression(PsiMethodReferenceExpression expression) {
|
||||
super.visitMethodReferenceExpression(expression);
|
||||
processFunctionalExpression(expression);
|
||||
}
|
||||
@Override
|
||||
public boolean visitReturnExpression(@NotNull UReturnExpression node) {
|
||||
if (refFrom instanceof RefMethodImpl) {
|
||||
RefMethodImpl refMethod = (RefMethodImpl)refFrom;
|
||||
refMethod.updateReturnValueTemplate(node.getReturnExpression());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void processFunctionalExpression(PsiFunctionalExpression expression) {
|
||||
final PsiClass aClass = PsiUtil.resolveClassInType(expression.getFunctionalInterfaceType());
|
||||
if (aClass != null) {
|
||||
refFrom.addReference(refFrom.getRefManager().getReference(aClass), aClass, psiFrom, false, true, null);
|
||||
final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(aClass);
|
||||
if (interfaceMethod != null) {
|
||||
refFrom.addReference(refFrom.getRefManager().getReference(interfaceMethod), interfaceMethod, psiFrom, false, true, null);
|
||||
refFrom.getRefManager().fireNodeMarkedReferenced(interfaceMethod, expression);
|
||||
}
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public boolean visitClassLiteralExpression(@NotNull UClassLiteralExpression node) {
|
||||
final PsiType type = node.getType();
|
||||
if (type instanceof PsiClassType) {
|
||||
processClassReference(((PsiClassType)type).resolve(), refFrom, decl, false);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private RefMethod processNewLikeConstruct(final PsiMethod psiConstructor, final PsiExpressionList argumentList) {
|
||||
RefMethodImpl refConstructor = (RefMethodImpl)refFrom.getRefManager().getReference(
|
||||
psiConstructor
|
||||
);
|
||||
refFrom.addReference(refConstructor, psiConstructor, psiFrom, false, true, null);
|
||||
private void processClassReference(final PsiClass psiClass,
|
||||
final RefJavaElementImpl refFrom,
|
||||
final UDeclaration from,
|
||||
boolean defaultConstructorOnly) {
|
||||
if (psiClass != null) {
|
||||
RefClassImpl refClass = (RefClassImpl)refFrom.getRefManager().getReference(psiClass.getNavigationElement());
|
||||
|
||||
if (argumentList != null) {
|
||||
PsiExpression[] psiParams = argumentList.getExpressions();
|
||||
for (PsiExpression param : psiParams) {
|
||||
param.accept(this);
|
||||
}
|
||||
if (refClass != null) {
|
||||
boolean hasConstructorsMarked = false;
|
||||
|
||||
if (refConstructor != null) {
|
||||
refConstructor.updateParameterValues(psiParams, psiConstructor);
|
||||
}
|
||||
}
|
||||
return refConstructor;
|
||||
}
|
||||
if (defaultConstructorOnly) {
|
||||
RefMethodImpl refDefaultConstructor = (RefMethodImpl)refClass.getDefaultConstructor();
|
||||
if (refDefaultConstructor != null) {
|
||||
refDefaultConstructor.addInReference(refFrom);
|
||||
refFrom.addOutReference(refDefaultConstructor);
|
||||
hasConstructorsMarked = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (RefMethod cons : refClass.getConstructors()) {
|
||||
if (cons instanceof RefImplicitConstructor) continue;
|
||||
((RefMethodImpl)cons).addInReference(refFrom);
|
||||
refFrom.addOutReference(cons);
|
||||
hasConstructorsMarked = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void visitClass(PsiClass psiClass) {
|
||||
super.visitClass(psiClass);
|
||||
RefClassImpl refClass = (RefClassImpl)refFrom.getRefManager().getReference(psiClass);
|
||||
refFrom.addReference(refClass, psiClass, psiFrom, false, true, null);
|
||||
}
|
||||
if (!hasConstructorsMarked) {
|
||||
refFrom.addReference(refClass, psiClass, from, false, true, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void visitReturnStatement(PsiReturnStatement statement) {
|
||||
super.visitReturnStatement(statement);
|
||||
|
||||
if (refFrom instanceof RefMethodImpl) {
|
||||
RefMethodImpl refMethod = (RefMethodImpl)refFrom;
|
||||
refMethod.updateReturnValueTemplate(statement.getReturnValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void visitClassObjectAccessExpression(PsiClassObjectAccessExpression expression) {
|
||||
super.visitClassObjectAccessExpression(expression);
|
||||
final PsiTypeElement operand = expression.getOperand();
|
||||
final PsiType type = operand.getType();
|
||||
if (type instanceof PsiClassType) {
|
||||
processClassReference(((PsiClassType)type).resolve(), refFrom, psiFrom, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void processClassReference(final PsiClass psiClass,
|
||||
final RefJavaElementImpl refFrom,
|
||||
final PsiModifierListOwner psiFrom,
|
||||
boolean defaultConstructorOnly) {
|
||||
if (psiClass != null) {
|
||||
RefClassImpl refClass = (RefClassImpl)refFrom.getRefManager().getReference(psiClass);
|
||||
|
||||
if (refClass != null) {
|
||||
boolean hasConstructorsMarked = false;
|
||||
|
||||
if (defaultConstructorOnly) {
|
||||
RefMethodImpl refDefaultConstructor = (RefMethodImpl)refClass.getDefaultConstructor();
|
||||
if (refDefaultConstructor != null) {
|
||||
refDefaultConstructor.addInReference(refFrom);
|
||||
refFrom.addOutReference(refDefaultConstructor);
|
||||
hasConstructorsMarked = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (RefMethod cons : refClass.getConstructors()) {
|
||||
if (cons instanceof RefImplicitConstructor) continue;
|
||||
((RefMethodImpl)cons).addInReference(refFrom);
|
||||
refFrom.addOutReference(cons);
|
||||
hasConstructorsMarked = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasConstructorsMarked) {
|
||||
refFrom.addReference(refClass, psiClass, psiFrom, false, true, null);
|
||||
}
|
||||
}
|
||||
private static void addClassReferenceForStaticImport(UExpression node,
|
||||
PsiMember psiResolved,
|
||||
RefJavaElementImpl refFrom, UDeclaration decl) {
|
||||
PsiElement sourcePsi = node.getSourcePsi();
|
||||
if (sourcePsi instanceof PsiReferenceExpression) {
|
||||
JavaResolveResult result = ((PsiReferenceExpression)sourcePsi).advancedResolve(false);
|
||||
if (result.getCurrentFileResolveScope() instanceof PsiImportStaticStatement) {
|
||||
final PsiClass containingClass = psiResolved.getContainingClass();
|
||||
if (containingClass != null) {
|
||||
RefElement refContainingClass = refFrom.getRefManager().getReference(containingClass);
|
||||
if (refContainingClass != null) {
|
||||
refFrom.addReference(refContainingClass, containingClass, decl, false, true, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private static PsiElement tryFindKotlinParameter(@NotNull UExpression node,
|
||||
@NotNull UDeclaration decl) {
|
||||
//TODO see KT-25524
|
||||
if (node instanceof UCallExpression && "invoke".equals(((UCallExpression)node).getMethodName())) {
|
||||
UIdentifier identifier = ((UCallExpression)node).getMethodIdentifier();
|
||||
if (identifier != null) {
|
||||
String name = identifier.getName();
|
||||
if (decl instanceof UMethod) {
|
||||
UParameter parameter = ((UMethod)decl).getUastParameters().stream().filter(p -> name.equals(p.getName())).findAny().orElse(null);
|
||||
if (parameter != null) {
|
||||
return parameter.getSourcePsi();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void updateRefMethod(PsiElement psiResolved,
|
||||
RefElement refResolved,
|
||||
PsiElement refExpression,
|
||||
final PsiElement psiFrom,
|
||||
UExpression refExpression,
|
||||
final UElement uFrom,
|
||||
final RefElement refFrom) {
|
||||
PsiMethod psiMethod = (PsiMethod)psiResolved;
|
||||
UMethod uMethod = ObjectUtils.notNull(UastContextKt.toUElement(psiResolved, UMethod.class));
|
||||
RefMethodImpl refMethod = (RefMethodImpl)refResolved;
|
||||
|
||||
if (refExpression instanceof PsiMethodReferenceExpression) {
|
||||
PsiType returnType = psiMethod.getReturnType();
|
||||
if (!psiMethod.isConstructor() &&
|
||||
!PsiType.VOID.equals(LambdaUtil.getFunctionalInterfaceReturnType((PsiFunctionalExpression)refExpression))) {
|
||||
if (refExpression instanceof UCallableReferenceExpression) {
|
||||
PsiType returnType = uMethod.getReturnType();
|
||||
if (!uMethod.isConstructor() &&
|
||||
!PsiType.VOID
|
||||
.equals(LambdaUtil.getFunctionalInterfaceReturnType(getFunctionalInterfaceType((UCallableReferenceExpression)refExpression)))) {
|
||||
refMethod.setReturnValueUsed(true);
|
||||
addTypeReference(psiFrom, returnType, refFrom.getRefManager());
|
||||
addTypeReference(uFrom, returnType, refFrom.getRefManager());
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (refExpression instanceof PsiLiteralExpression){ //references in literal expressions
|
||||
PsiType returnType = psiMethod.getReturnType();
|
||||
if (!psiMethod.isConstructor() && !PsiType.VOID.equals(returnType)) {
|
||||
if (refExpression instanceof ULiteralExpression) { //references in literal expressions
|
||||
PsiType returnType = uMethod.getReturnType();
|
||||
if (!uMethod.isConstructor() && !PsiType.VOID.equals(returnType)) {
|
||||
refMethod.setReturnValueUsed(true);
|
||||
addTypeReference(psiFrom, returnType, refFrom.getRefManager());
|
||||
addTypeReference(uFrom, returnType, refFrom.getRefManager());
|
||||
}
|
||||
return;
|
||||
}
|
||||
PsiMethodCallExpression call = PsiTreeUtil.getParentOfType(
|
||||
refExpression,
|
||||
PsiMethodCallExpression.class
|
||||
);
|
||||
|
||||
UCallExpression call = null;
|
||||
if (refExpression instanceof UCallExpression) {
|
||||
call = (UCallExpression)refExpression;
|
||||
}
|
||||
else if (refExpression instanceof UQualifiedReferenceExpression) {
|
||||
call = ObjectUtils.tryCast(((UQualifiedReferenceExpression)refExpression).getSelector(), UCallExpression.class);
|
||||
}
|
||||
if (call != null) {
|
||||
PsiType returnType = psiMethod.getReturnType();
|
||||
if (!psiMethod.isConstructor() && !PsiType.VOID.equals(returnType)) {
|
||||
if (!ExpressionUtils.isVoidContext(call)) {
|
||||
PsiType returnType = uMethod.getReturnType();
|
||||
if (!uMethod.isConstructor() && !PsiType.VOID.equals(returnType)) {
|
||||
PsiExpression expression = ObjectUtils.tryCast(call.getJavaPsi(), PsiExpression.class);
|
||||
if (expression == null || !ExpressionUtils.isVoidContext(expression)) {
|
||||
refMethod.setReturnValueUsed(true);
|
||||
}
|
||||
|
||||
addTypeReference(psiFrom, returnType, refFrom.getRefManager());
|
||||
addTypeReference(uFrom, returnType, refFrom.getRefManager());
|
||||
}
|
||||
|
||||
PsiExpressionList argumentList = call.getArgumentList();
|
||||
List<UExpression> argumentList = call.getValueArguments();
|
||||
if (!argumentList.isEmpty()) {
|
||||
refMethod.updateParameterValues(argumentList.getExpressions(), psiMethod);
|
||||
refMethod.updateParameterValues(argumentList, psiResolved);
|
||||
}
|
||||
|
||||
final PsiExpression psiExpression = call.getMethodExpression().getQualifierExpression();
|
||||
if (psiExpression != null) {
|
||||
final PsiType usedType = psiExpression.getType();
|
||||
final UExpression uExpression = call.getReceiver();
|
||||
if (uExpression != null) {
|
||||
final PsiType usedType = uExpression.getExpressionType();
|
||||
if (usedType != null) {
|
||||
final String fqName = psiMethod.getContainingClass().getQualifiedName();
|
||||
if (fqName != null) {
|
||||
final PsiClassType methodOwnerType = JavaPsiFacade.getInstance(call.getProject()).getElementFactory()
|
||||
.createTypeByFQClassName(fqName, GlobalSearchScope.allScope(psiMethod.getProject()));
|
||||
if (!usedType.equals(methodOwnerType)) {
|
||||
refMethod.setCalledOnSubClass(true);
|
||||
UClass containingClass = UDeclarationKt.getContainingDeclaration(uMethod, UClass.class);
|
||||
final String fqName;
|
||||
if (containingClass != null) {
|
||||
fqName = containingClass.getQualifiedName();
|
||||
if (fqName != null) {
|
||||
final PsiClassType methodOwnerType = JavaPsiFacade.getInstance(psiResolved.getProject()).getElementFactory()
|
||||
.createTypeByFQClassName(fqName, GlobalSearchScope.allScope(psiResolved.getProject()));
|
||||
if (!usedType.equals(methodOwnerType)) {
|
||||
refMethod.setCalledOnSubClass(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -276,8 +421,13 @@ public class RefJavaUtilImpl extends RefJavaUtil{
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static PsiType getFunctionalInterfaceType(UCallableReferenceExpression expression) {
|
||||
PsiElement psi = expression.getSourcePsi();
|
||||
if (psi instanceof PsiFunctionalExpression) {
|
||||
return ((PsiFunctionalExpression)psi).getFunctionalInterfaceType();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RefClass getTopLevelClass(@NotNull RefElement refElement) {
|
||||
@@ -316,135 +466,139 @@ public class RefJavaUtilImpl extends RefJavaUtil{
|
||||
@NotNull
|
||||
@Override
|
||||
public String getAccessModifier(@NotNull PsiModifierListOwner psiElement) {
|
||||
if (psiElement instanceof PsiParameter) return PsiModifier.PACKAGE_LOCAL;
|
||||
if (psiElement instanceof PsiParameter) return PsiModifier.PACKAGE_LOCAL;
|
||||
|
||||
PsiModifierList list = psiElement.getModifierList();
|
||||
String result = PsiModifier.PACKAGE_LOCAL;
|
||||
PsiModifierList list = psiElement.getModifierList();
|
||||
String result = PsiModifier.PACKAGE_LOCAL;
|
||||
|
||||
if (list != null) {
|
||||
if (list.hasModifierProperty(PsiModifier.PRIVATE)) {
|
||||
result = PsiModifier.PRIVATE;
|
||||
}
|
||||
else if (list.hasModifierProperty(PsiModifier.PROTECTED)) {
|
||||
result = PsiModifier.PROTECTED;
|
||||
}
|
||||
else if (list.hasModifierProperty(PsiModifier.PUBLIC)) {
|
||||
result = PsiModifier.PUBLIC;
|
||||
}
|
||||
else if (psiElement.getParent() instanceof PsiClass) {
|
||||
PsiClass ownerClass = (PsiClass)psiElement.getParent();
|
||||
if (ownerClass.isInterface()) {
|
||||
result = PsiModifier.PUBLIC;
|
||||
}
|
||||
if (ownerClass.isEnum() && result.equals(PsiModifier.PACKAGE_LOCAL)) {
|
||||
result = PsiModifier.PRIVATE;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (list != null) {
|
||||
if (list.hasModifierProperty(PsiModifier.PRIVATE)) {
|
||||
result = PsiModifier.PRIVATE;
|
||||
}
|
||||
else if (list.hasModifierProperty(PsiModifier.PROTECTED)) {
|
||||
result = PsiModifier.PROTECTED;
|
||||
}
|
||||
else if (list.hasModifierProperty(PsiModifier.PUBLIC)) {
|
||||
result = PsiModifier.PUBLIC;
|
||||
}
|
||||
else if (psiElement.getParent() instanceof PsiClass) {
|
||||
PsiClass ownerClass = (PsiClass)psiElement.getParent();
|
||||
if (ownerClass.isInterface()) {
|
||||
result = PsiModifier.PUBLIC;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable public RefClass getOwnerClass(RefManager refManager, PsiElement psiElement) {
|
||||
while (psiElement != null && !(psiElement instanceof PsiClass)) {
|
||||
psiElement = psiElement.getParent();
|
||||
}
|
||||
@Override
|
||||
@Nullable
|
||||
public RefClass getOwnerClass(RefManager refManager, UElement uElement) {
|
||||
while (uElement != null && !(uElement instanceof UClass)) {
|
||||
uElement = uElement.getUastParent();
|
||||
}
|
||||
|
||||
return psiElement != null ? (RefClass)refManager.getReference(psiElement) : null;
|
||||
}
|
||||
return uElement != null ? (RefClass)refManager.getReference(uElement.getSourcePsi()) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable public RefClass getOwnerClass(RefElement refElement) {
|
||||
RefEntity parent = refElement.getOwner();
|
||||
@Override
|
||||
@Nullable
|
||||
public RefClass getOwnerClass(RefElement refElement) {
|
||||
RefEntity parent = refElement.getOwner();
|
||||
|
||||
while (!(parent instanceof RefClass) && parent instanceof RefElement) {
|
||||
parent = parent.getOwner();
|
||||
}
|
||||
while (!(parent instanceof RefClass) && parent instanceof RefElement) {
|
||||
parent = parent.getOwner();
|
||||
}
|
||||
|
||||
if (parent instanceof RefClass) return (RefClass)parent;
|
||||
if (parent instanceof RefClass) return (RefClass)parent;
|
||||
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isMethodOnlyCallsSuper(UMethod method) {
|
||||
PsiMethod javaMethod = method.getJavaPsi();
|
||||
boolean hasStatements = false;
|
||||
UExpression body = method.getUastBody();
|
||||
if (body != null) {
|
||||
List<UExpression> statements =
|
||||
body instanceof UBlockExpression ? ((UBlockExpression)body).getExpressions() : Collections.singletonList(body);
|
||||
for (UExpression expression : statements) {
|
||||
boolean isCallToSameSuper = false;
|
||||
if (expression instanceof UReturnExpression) {
|
||||
UExpression returnExpr = ((UReturnExpression)expression).getReturnExpression();
|
||||
isCallToSameSuper = returnExpr == null || isCallToSuperMethod(returnExpr, method);
|
||||
}
|
||||
else if (!(expression instanceof UBlockExpression)) {
|
||||
isCallToSameSuper = isCallToSuperMethod(expression, method);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMethodOnlyCallsSuper(PsiMethod method) {
|
||||
boolean hasStatements = false;
|
||||
PsiCodeBlock body = method.getBody();
|
||||
if (body != null) {
|
||||
PsiStatement[] statements = body.getStatements();
|
||||
for (PsiStatement statement : statements) {
|
||||
boolean isCallToSameSuper = false;
|
||||
if (statement instanceof PsiExpressionStatement) {
|
||||
isCallToSameSuper = isCallToSuperMethod(((PsiExpressionStatement)statement).getExpression(), method);
|
||||
}
|
||||
else if (statement instanceof PsiReturnStatement) {
|
||||
PsiExpression expression = ((PsiReturnStatement)statement).getReturnValue();
|
||||
isCallToSameSuper = expression == null || isCallToSuperMethod(expression, method);
|
||||
}
|
||||
hasStatements = true;
|
||||
if (isCallToSameSuper) continue;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
hasStatements = true;
|
||||
if (isCallToSameSuper) continue;
|
||||
if (hasStatements) {
|
||||
final PsiMethod[] superMethods = javaMethod.findSuperMethods();
|
||||
for (PsiMethod superMethod : superMethods) {
|
||||
if (VisibilityUtil.compare(VisibilityUtil.getVisibilityModifier(superMethod.getModifierList()),
|
||||
VisibilityUtil.getVisibilityModifier(javaMethod.getModifierList())) > 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return hasStatements;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public boolean isCallToSuperMethod(UExpression expression, UMethod method) {
|
||||
if (expression instanceof UQualifiedReferenceExpression) {
|
||||
UExpression receiver = ((UQualifiedReferenceExpression)expression).getReceiver();
|
||||
UExpression selector = ((UQualifiedReferenceExpression)expression).getSelector();
|
||||
|
||||
if (hasStatements) {
|
||||
final PsiMethod[] superMethods = method.findSuperMethods();
|
||||
for (PsiMethod superMethod : superMethods) {
|
||||
if (VisibilityUtil.compare(VisibilityUtil.getVisibilityModifier(superMethod.getModifierList()),
|
||||
VisibilityUtil.getVisibilityModifier(method.getModifierList())) > 0) return false;
|
||||
}
|
||||
}
|
||||
return hasStatements;
|
||||
}
|
||||
if (receiver instanceof USuperExpression && selector instanceof UCallExpression) {
|
||||
PsiMethod superMethod = ((UCallExpression)selector).resolve();
|
||||
if (superMethod == null || !MethodSignatureUtil.areSignaturesEqual(method.getJavaPsi(), superMethod)) return false;
|
||||
|
||||
@Override
|
||||
public boolean isCallToSuperMethod(PsiExpression expression, PsiMethod method) {
|
||||
if (expression instanceof PsiMethodCallExpression) {
|
||||
PsiMethodCallExpression methodCall = (PsiMethodCallExpression)expression;
|
||||
if (methodCall.getMethodExpression().getQualifierExpression() instanceof PsiSuperExpression) {
|
||||
PsiMethod superMethod = (PsiMethod)methodCall.getMethodExpression().resolve();
|
||||
if (superMethod == null || !MethodSignatureUtil.areSignaturesEqual(method, superMethod)) return false;
|
||||
PsiExpression[] args = methodCall.getArgumentList().getExpressions();
|
||||
PsiParameter[] parms = method.getParameterList().getParameters();
|
||||
List<UExpression> args = ((UCallExpression)selector).getValueArguments();
|
||||
List<UParameter> params = method.getUastParameters();
|
||||
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
PsiExpression arg = args[i];
|
||||
if (!(arg instanceof PsiReferenceExpression)) return false;
|
||||
if (!parms[i].equals(((PsiReferenceExpression)arg).resolve())) return false;
|
||||
}
|
||||
for (int i = 0; i < args.size(); i++) {
|
||||
UExpression arg = args.get(i);
|
||||
if (!(arg instanceof USimpleNameReferenceExpression)) return false;
|
||||
if (!params.get(i).equals(((USimpleNameReferenceExpression)arg).resolve())) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareAccess(String a1, String a2) {
|
||||
return Integer.compare(getAccessNumber(a1), getAccessNumber(a2));
|
||||
}
|
||||
@Override
|
||||
public int compareAccess(String a1, String a2) {
|
||||
return Integer.compare(getAccessNumber(a1), getAccessNumber(a2));
|
||||
}
|
||||
|
||||
@SuppressWarnings("StringEquality")
|
||||
private static int getAccessNumber(String a) {
|
||||
if (a == PsiModifier.PRIVATE) {
|
||||
return 0;
|
||||
}
|
||||
if (a == PsiModifier.PACKAGE_LOCAL) {
|
||||
return 1;
|
||||
}
|
||||
if (a == PsiModifier.PROTECTED) {
|
||||
return 2;
|
||||
}
|
||||
if (a == PsiModifier.PUBLIC) return 3;
|
||||
@SuppressWarnings("StringEquality")
|
||||
private static int getAccessNumber(String a) {
|
||||
if (a == PsiModifier.PRIVATE) {
|
||||
return 0;
|
||||
}
|
||||
if (a == PsiModifier.PACKAGE_LOCAL) {
|
||||
return 1;
|
||||
}
|
||||
if (a == PsiModifier.PROTECTED) {
|
||||
return 2;
|
||||
}
|
||||
if (a == PsiModifier.PUBLIC) return 3;
|
||||
|
||||
return -1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAccessModifier(@NotNull RefJavaElement refElement, @NotNull String newAccess) {
|
||||
@@ -462,14 +616,14 @@ public class RefJavaUtilImpl extends RefJavaUtil{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTypeReference(PsiElement psiElement, PsiType psiType, RefManager refManager) {
|
||||
addTypeReference(psiElement, psiType, refManager, null);
|
||||
public void addTypeReference(UElement uElement, PsiType psiType, RefManager refManager) {
|
||||
addTypeReference(uElement, psiType, refManager, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTypeReference(PsiElement psiElement, PsiType psiType, RefManager refManager, @Nullable RefJavaElement refMethod) {
|
||||
public void addTypeReference(UElement uElement, PsiType psiType, RefManager refManager, @Nullable RefJavaElement refMethod) {
|
||||
if (psiType != null) {
|
||||
final RefClass ownerClass = getOwnerClass(refManager, psiElement);
|
||||
final RefClass ownerClass = getOwnerClass(refManager, uElement);
|
||||
if (ownerClass != null) {
|
||||
psiType = psiType.getDeepComponentType();
|
||||
if (psiType instanceof PsiClassType) {
|
||||
@@ -484,10 +638,44 @@ public class RefJavaUtilImpl extends RefJavaUtil{
|
||||
}
|
||||
}
|
||||
else {
|
||||
((RefManagerImpl)refManager).fireNodeMarkedReferenced(psiClass, psiElement);
|
||||
((RefManagerImpl)refManager).fireNodeMarkedReferenced(psiClass, uElement.getSourcePsi());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isAccessedForWriting(@NotNull UElement expression) {
|
||||
if (isOnAssignmentLeftHand(expression)) return true;
|
||||
UElement parent = skipParenthesises(expression);
|
||||
return isIncrementDecrement(parent);
|
||||
}
|
||||
|
||||
private static boolean isIncrementDecrement(UElement element) {
|
||||
if (!(element instanceof UUnaryExpression)) return false;
|
||||
UastOperator operator = ((UUnaryExpression)element).getOperator();
|
||||
return operator == UastPostfixOperator.DEC
|
||||
|| operator == UastPostfixOperator.INC
|
||||
|| operator == UastPrefixOperator.DEC
|
||||
|| operator == UastPrefixOperator.INC;
|
||||
}
|
||||
|
||||
private static boolean isAccessedForReading(@NotNull UElement expression) {
|
||||
UElement parent = skipParenthesises(expression);
|
||||
return !(parent instanceof UBinaryExpression) ||
|
||||
!(((UBinaryExpression)parent).getOperator() instanceof UastBinaryOperator.AssignOperator) ||
|
||||
UastUtils.isChildOf(((UBinaryExpression)parent).getRightOperand(), expression, false);
|
||||
}
|
||||
|
||||
private static boolean isOnAssignmentLeftHand(@NotNull UElement expression) {
|
||||
UExpression parent = ObjectUtils.tryCast(skipParenthesises(expression), UExpression.class);
|
||||
if (parent == null) return false;
|
||||
return parent instanceof UBinaryExpression
|
||||
&& ((UBinaryExpression)parent).getOperator() instanceof UastBinaryOperator.AssignOperator
|
||||
&& UastUtils.isChildOf(expression, ((UBinaryExpression)parent).getLeftOperand(), false);
|
||||
}
|
||||
|
||||
private static UElement skipParenthesises(@NotNull UElement expression) {
|
||||
return UastUtils.skipParentOfType(expression, true, UParenthesizedExpression.class);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user