mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-13 09:19:13 +07:00
IDEA-68584 Completion should detect return type and autocast in some cases
This commit is contained in:
@@ -30,28 +30,23 @@ import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.RangeMarker;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Condition;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.NullableLazyKey;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.util.*;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.patterns.PsiElementPattern;
|
||||
import com.intellij.patterns.PsiJavaPatterns;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.*;
|
||||
import com.intellij.psi.filters.AndFilter;
|
||||
import com.intellij.psi.filters.ClassFilter;
|
||||
import com.intellij.psi.filters.ElementFilter;
|
||||
import com.intellij.psi.filters.element.ExcludeDeclaredFilter;
|
||||
import com.intellij.psi.filters.element.ExcludeSillyAssignment;
|
||||
import com.intellij.psi.html.HtmlTag;
|
||||
import com.intellij.psi.impl.FakePsiElement;
|
||||
import com.intellij.psi.impl.light.LightVariableBuilder;
|
||||
import com.intellij.psi.impl.source.PostprocessReformattingAspect;
|
||||
import com.intellij.psi.impl.source.PsiImmediateClassType;
|
||||
import com.intellij.psi.javadoc.PsiDocToken;
|
||||
import com.intellij.psi.scope.BaseScopeProcessor;
|
||||
import com.intellij.psi.scope.ElementClassFilter;
|
||||
import com.intellij.psi.scope.ElementClassHint;
|
||||
import com.intellij.psi.scope.NameHint;
|
||||
import com.intellij.psi.scope.PsiScopeProcessor;
|
||||
import com.intellij.psi.scope.util.PsiScopesUtil;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.util.PsiFormatUtil;
|
||||
@@ -78,13 +73,7 @@ public class JavaCompletionUtil {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.completion.JavaCompletionUtil");
|
||||
public static final Key<PairFunction<PsiExpression, CompletionParameters, PsiType>> DYNAMIC_TYPE_EVALUATOR = Key.create("DYNAMIC_TYPE_EVALUATOR");
|
||||
|
||||
static final Key<PsiType> QUALIFIER_TYPE_ATTR = Key.create("qualifierType"); // SmartPsiElementPointer to PsiType of "qualifier"
|
||||
@NonNls
|
||||
public static final String GET_PREFIX = "get";
|
||||
@NonNls
|
||||
public static final String SET_PREFIX = "set";
|
||||
@NonNls
|
||||
public static final String IS_PREFIX = "is";
|
||||
private static final Key<PsiType> QUALIFIER_TYPE_ATTR = Key.create("qualifierType"); // SmartPsiElementPointer to PsiType of "qualifier"
|
||||
public static final OffsetKey LPAREN_OFFSET = OffsetKey.create("lparen");
|
||||
public static final OffsetKey RPAREN_OFFSET = OffsetKey.create("rparen");
|
||||
public static final OffsetKey ARG_LIST_END_OFFSET = OffsetKey.create("argListEnd");
|
||||
@@ -435,20 +424,26 @@ public class JavaCompletionUtil {
|
||||
boolean checkInitialized = parameters.getInvocationCount() <= 1 && call != null && PsiKeyword.SUPER.equals(call.getMethodExpression().getText());
|
||||
|
||||
final JavaCompletionProcessor processor = new JavaCompletionProcessor(element, elementFilter, checkAccess, checkInitialized, filterStaticAfterInstance, nameCondition);
|
||||
final PsiType plainQualifier = processor.getQualifierType();
|
||||
PsiType qualifierType = plainQualifier;
|
||||
|
||||
PsiType runtimeQualifier = getQualifierCastType(javaReference, parameters);
|
||||
if (runtimeQualifier != null) {
|
||||
javaReference = (PsiReferenceExpression) JavaPsiFacade.getElementFactory(element.getProject()).createExpressionFromText("xxx.xxx", createContextWithXxxVariable(element, runtimeQualifier));
|
||||
qualifierType = runtimeQualifier;
|
||||
}
|
||||
|
||||
javaReference.processVariants(processor);
|
||||
final Collection<CompletionElement> plainResults = processor.getResults();
|
||||
|
||||
final PsiType qualifierType = processor.getQualifierType();
|
||||
PsiType castedQualifierType = addQualifierCastingVariants(javaReference, processor, set, parameters);
|
||||
|
||||
boolean mayHighlight = qualifierType != null && (castedQualifierType == null || castedQualifierType.equals(qualifierType) || !qualifierType.isAssignableFrom(castedQualifierType));
|
||||
final PsiTypeLookupItem castItem = runtimeQualifier == null ? null : PsiTypeLookupItem.createLookupItem(runtimeQualifier, (PsiReferenceExpression)javaReference);
|
||||
|
||||
final boolean pkgContext = inSomePackage(element);
|
||||
|
||||
final Set<PsiMember> mentioned = new THashSet<PsiMember>();
|
||||
for (CompletionElement completionElement : plainResults) {
|
||||
LookupElement item = createLookupElement(completionElement, qualifierType);
|
||||
for (CompletionElement completionElement : processor.getResults()) {
|
||||
LookupElement item = createLookupElement(completionElement);
|
||||
if (item != null) {
|
||||
item.putUserData(QUALIFIER_TYPE_ATTR, qualifierType);
|
||||
final Object o = item.getObject();
|
||||
if (o instanceof PsiClass && !isSourceLevelAccessible(element, (PsiClass)o, pkgContext)) {
|
||||
continue;
|
||||
@@ -459,7 +454,7 @@ public class JavaCompletionUtil {
|
||||
}
|
||||
mentioned.add((PsiMember)o);
|
||||
}
|
||||
set.add(mayHighlight ? highlightIfNeeded(qualifierType, item, o) : item);
|
||||
set.add(highlightIfNeeded(qualifierType, castQualifier(item, castItem, plainQualifier), o));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -479,68 +474,63 @@ public class JavaCompletionUtil {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiType addQualifierCastingVariants(PsiJavaReference javaReference, JavaCompletionProcessor processor, THashSet<LookupElement> set, CompletionParameters parameters) {
|
||||
private static PsiType getQualifierCastType(PsiJavaReference javaReference, CompletionParameters parameters) {
|
||||
if (javaReference instanceof PsiReferenceExpression) {
|
||||
final PsiReferenceExpression refExpr = (PsiReferenceExpression)javaReference;
|
||||
final PsiExpression qualifier = refExpr.getQualifierExpression();
|
||||
if (qualifier != null) {
|
||||
final Project project = qualifier.getProject();
|
||||
final PairFunction<PsiExpression, CompletionParameters, PsiType> evaluator = refExpr.getContainingFile().getCopyableUserData(DYNAMIC_TYPE_EVALUATOR);
|
||||
PsiReferenceExpression context = refExpr;
|
||||
PsiType type = null;
|
||||
final PairFunction<PsiExpression, CompletionParameters, PsiType> evaluator = refExpr.getContainingFile().getCopyableUserData(DYNAMIC_TYPE_EVALUATOR);
|
||||
if (evaluator != null) {
|
||||
type = evaluator.fun(qualifier, parameters);
|
||||
context = null;
|
||||
}
|
||||
if (type == null) {
|
||||
type = GuessManager.getInstance(project).getControlFlowExpressionType(qualifier);
|
||||
context = refExpr;
|
||||
}
|
||||
if (type != null) {
|
||||
processor.clear();
|
||||
|
||||
return addQualifierCastingVariants(processor, refExpr, type, set, context);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static PsiType addQualifierCastingVariants(JavaCompletionProcessor processor, PsiReferenceExpression refExpr,
|
||||
PsiType castTo,
|
||||
THashSet<LookupElement> set, final PsiReferenceExpression context) {
|
||||
Project project = refExpr.getProject();
|
||||
|
||||
PsiExpression qualifier = refExpr.getQualifierExpression();
|
||||
assert qualifier != null;
|
||||
final String newText = "((" + castTo.getCanonicalText() + ") " + qualifier.getText() + ")." + refExpr.getReferenceName();
|
||||
final PsiExpression newRef = JavaPsiFacade.getElementFactory(project).createExpressionFromText(newText, context);
|
||||
((PsiReferenceExpression)newRef).processVariants(processor);
|
||||
|
||||
final LookupElement castItem = PsiTypeLookupItem.createLookupItem(castTo, refExpr);
|
||||
|
||||
for (CompletionElement completionElement : processor.getResults()) {
|
||||
final LookupElement item = createLookupElement(completionElement, castTo);
|
||||
if (item != null) {
|
||||
LookupElement item1 = castQualifier(project, item, castItem);
|
||||
set.add(highlightIfNeeded(castTo, item1, item1.getObject()));
|
||||
@NotNull
|
||||
private static LookupElement castQualifier(@NotNull LookupElement item,
|
||||
@Nullable final PsiTypeLookupItem castTypeItem,
|
||||
@Nullable PsiType plainQualifier) {
|
||||
if (castTypeItem == null) {
|
||||
return item;
|
||||
}
|
||||
if (plainQualifier != null) {
|
||||
Object o = item.getObject();
|
||||
if (containsMember(plainQualifier, o)) {
|
||||
if (!(o instanceof PsiMethod)) {
|
||||
return item;
|
||||
}
|
||||
PsiType castType = castTypeItem.getPsiType();
|
||||
if (plainQualifier instanceof PsiClassType && castType instanceof PsiClassType) {
|
||||
PsiMethod method = (PsiMethod)o;
|
||||
PsiSubstitutor plainSub = ((PsiClassType)plainQualifier).resolveGenerics().getSubstitutor();
|
||||
PsiSubstitutor castSub = ((PsiClassType)castType).resolveGenerics().getSubstitutor();
|
||||
if (method.getSignature(plainSub).equals(method.getSignature(castSub)) &&
|
||||
plainSub.substitute(method.getReturnType()).equals(castSub.substitute(method.getReturnType()))) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return castTo;
|
||||
}
|
||||
|
||||
private static LookupElementDecorator<LookupElement> castQualifier(final Project project, LookupElement item, final LookupElement to) {
|
||||
return LookupElementDecorator.withInsertHandler(item, new InsertHandlerDecorator<LookupElement>() {
|
||||
public void handleInsert(InsertionContext context, LookupElementDecorator<LookupElement> item) {
|
||||
final Document document = context.getEditor().getDocument();
|
||||
PsiDocumentManager.getInstance(project).commitDocument(document);
|
||||
context.commitDocument();
|
||||
final PsiFile file = context.getFile();
|
||||
final PsiJavaCodeReferenceElement ref =
|
||||
PsiTreeUtil.findElementOfClassAtOffset(file, context.getStartOffset(), PsiJavaCodeReferenceElement.class, false);
|
||||
if (ref != null) {
|
||||
final PsiElement qualifier = ref.getQualifier();
|
||||
if (qualifier != null) {
|
||||
final CodeStyleSettings settings = CodeStyleSettingsManager.getSettings(project);
|
||||
final CodeStyleSettings settings = CodeStyleSettingsManager.getSettings(qualifier.getProject());
|
||||
|
||||
final String parenSpace = settings.SPACE_WITHIN_PARENTHESES ? " " : "";
|
||||
document.insertString(qualifier.getTextRange().getEndOffset(), parenSpace + ")");
|
||||
@@ -551,7 +541,7 @@ public class JavaCompletionUtil {
|
||||
final int exprStart = qualifier.getTextRange().getStartOffset();
|
||||
document.insertString(exprStart, prefix + spaceWithin + ")" + spaceAfter);
|
||||
|
||||
CompletionUtil.emulateInsertion(context, exprStart + prefix.length(), to);
|
||||
CompletionUtil.emulateInsertion(context, exprStart + prefix.length(), castTypeItem);
|
||||
context.getEditor().getCaretModel().moveToOffset(context.getTailOffset());
|
||||
}
|
||||
}
|
||||
@@ -562,24 +552,26 @@ public class JavaCompletionUtil {
|
||||
}
|
||||
|
||||
public static LookupElement highlightIfNeeded(PsiType qualifierType, LookupElement item, Object object) {
|
||||
if (qualifierType instanceof PsiArrayType) {
|
||||
if (object instanceof PsiField || object instanceof PsiMethod) { //length and clone()
|
||||
PsiElement parent = ((PsiElement)object).getParent();
|
||||
if (parent instanceof PsiClass && parent.getContainingFile().getVirtualFile() == null) { //yes, they're a bit dummy
|
||||
return highlight(item);
|
||||
}
|
||||
return containsMember(qualifierType, object) ? highlight(item) : item;
|
||||
}
|
||||
|
||||
public static boolean containsMember(PsiType qualifierType, Object object) {
|
||||
if (qualifierType instanceof PsiArrayType && object instanceof PsiMember) { //length and clone()
|
||||
if (((PsiMember)object).getContainingFile().getVirtualFile() == null) { //yes, they're a bit dummy
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (qualifierType instanceof PsiClassType) {
|
||||
PsiClass qualifierClass = ((PsiClassType)qualifierType).resolve();
|
||||
if (object instanceof PsiField || object instanceof PsiMethod || object instanceof PsiClass) {
|
||||
PsiClass parent = ((PsiMember)object).getContainingClass();
|
||||
if (parent != null && parent.equals(qualifierClass)) {
|
||||
return highlight(item);
|
||||
}
|
||||
if (qualifierClass == null) return false;
|
||||
if (object instanceof PsiMethod) {
|
||||
return qualifierClass.findMethodBySignature((PsiMethod)object, false) != null;
|
||||
}
|
||||
if (object instanceof PsiMember) {
|
||||
return qualifierClass.equals(((PsiMember)object).getContainingClass());
|
||||
}
|
||||
}
|
||||
return item;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static LookupElement highlight(LookupElement decorator) {
|
||||
@@ -593,7 +585,7 @@ public class JavaCompletionUtil {
|
||||
}), 1);
|
||||
}
|
||||
|
||||
private static LookupItem<?> createLookupElement(CompletionElement completionElement, PsiType qualifierType) {
|
||||
private static LookupElement createLookupElement(CompletionElement completionElement) {
|
||||
Object completion = completionElement.getElement();
|
||||
assert !(completion instanceof LookupElement);
|
||||
|
||||
@@ -601,14 +593,12 @@ public class JavaCompletionUtil {
|
||||
LookupElement _ret = LookupItemUtil.objectToLookupItem(completion);
|
||||
if (_ret == null || !(_ret instanceof LookupItem)) return null;
|
||||
|
||||
LookupItem<?> ret = (LookupItem<?>)_ret;
|
||||
final PsiSubstitutor substitutor = completionElement.getSubstitutor();
|
||||
if (substitutor != null) {
|
||||
ret.setAttribute(LookupItem.SUBSTITUTOR, substitutor);
|
||||
((LookupItem<?>)_ret).setAttribute(LookupItem.SUBSTITUTOR, substitutor);
|
||||
}
|
||||
ret.putUserData(QUALIFIER_TYPE_ATTR, qualifierType);
|
||||
|
||||
return ret;
|
||||
return _ret;
|
||||
}
|
||||
|
||||
public static boolean hasAccessibleConstructor(PsiType type) {
|
||||
@@ -928,4 +918,21 @@ public class JavaCompletionUtil {
|
||||
context.setAddCompletionChar(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static FakePsiElement createContextWithXxxVariable(final PsiElement place, final PsiType varType) {
|
||||
return new FakePsiElement() {
|
||||
@Override
|
||||
public boolean processDeclarations(@NotNull PsiScopeProcessor processor,
|
||||
@NotNull ResolveState state,
|
||||
PsiElement lastParent,
|
||||
@NotNull PsiElement place) {
|
||||
return processor.execute(new LightVariableBuilder("xxx", varType, place), ResolveState.initial());
|
||||
}
|
||||
|
||||
@Override
|
||||
public PsiElement getParent() {
|
||||
return place;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+2
-21
@@ -33,11 +33,8 @@ import com.intellij.psi.codeStyle.JavaCodeStyleManager;
|
||||
import com.intellij.psi.filters.*;
|
||||
import com.intellij.psi.filters.element.ModifierFilter;
|
||||
import com.intellij.psi.filters.types.AssignableFromFilter;
|
||||
import com.intellij.psi.impl.FakePsiElement;
|
||||
import com.intellij.psi.impl.light.LightVariableBuilder;
|
||||
import com.intellij.psi.impl.source.PostprocessReformattingAspect;
|
||||
import com.intellij.psi.infos.CandidateInfo;
|
||||
import com.intellij.psi.scope.PsiScopeProcessor;
|
||||
import com.intellij.psi.util.InheritanceUtil;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
@@ -445,24 +442,8 @@ public class ReferenceExpressionCompletionContributor {
|
||||
.createExpressionFromText(((PsiClass)qualifierItem.getObject()).getQualifiedName() + ".xxx", place);
|
||||
}
|
||||
|
||||
return (PsiReferenceExpression) factory.createExpressionFromText("xxx.xxx", createContextWithXxxVariable(place, qualifierType));
|
||||
}
|
||||
|
||||
public static FakePsiElement createContextWithXxxVariable(final PsiElement place, final PsiType varType) {
|
||||
return new FakePsiElement() {
|
||||
@Override
|
||||
public boolean processDeclarations(@NotNull PsiScopeProcessor processor,
|
||||
@NotNull ResolveState state,
|
||||
PsiElement lastParent,
|
||||
@NotNull PsiElement place) {
|
||||
return processor.execute(new LightVariableBuilder("xxx", varType, place), ResolveState.initial());
|
||||
}
|
||||
|
||||
@Override
|
||||
public PsiElement getParent() {
|
||||
return place;
|
||||
}
|
||||
};
|
||||
return (PsiReferenceExpression) factory.createExpressionFromText("xxx.xxx", JavaCompletionUtil
|
||||
.createContextWithXxxVariable(place, qualifierType));
|
||||
}
|
||||
|
||||
private static boolean shoudChain(PsiElement element, PsiType qualifierType, PsiType expectedType, LookupElement item) {
|
||||
|
||||
+1
-1
@@ -85,7 +85,7 @@ public class JavaCompletionProcessor extends BaseScopeProcessor implements Eleme
|
||||
if (qualifier instanceof PsiSuperExpression) {
|
||||
final PsiJavaCodeReferenceElement qSuper = ((PsiSuperExpression)qualifier).getQualifier();
|
||||
if (qSuper == null) {
|
||||
myQualifierClass = JavaResolveUtil.getContextClass(myElement);
|
||||
myQualifierClass = JavaResolveUtil.getContextClass( myElement);
|
||||
} else {
|
||||
final PsiElement target = qSuper.resolve();
|
||||
myQualifierClass = target instanceof PsiClass ? (PsiClass)target : null;
|
||||
|
||||
@@ -46,6 +46,7 @@ public class PsiTypeLookupItem extends LookupItem {
|
||||
myBracketsCount = bracketsCount;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public PsiType getPsiType() {
|
||||
Object object = getObject();
|
||||
PsiType type = object instanceof PsiType ? (PsiType)object : JavaPsiFacade.getElementFactory(((PsiClass) object).getProject()).createType((PsiClass)object);
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import java.lang.Object;
|
||||
|
||||
class IFoo {
|
||||
Object getValue() {}
|
||||
}
|
||||
|
||||
class Foo extends IFoo {
|
||||
Foo getValue() {}
|
||||
void foo(IFoo o) {
|
||||
if (o instanceof Foo) {
|
||||
o.getv<caret>x
|
||||
}
|
||||
}
|
||||
}
|
||||
+8
@@ -1300,4 +1300,12 @@ public class ListUtils {
|
||||
checkResult()
|
||||
}
|
||||
|
||||
public void testCastVisually() {
|
||||
configure()
|
||||
def p = LookupElementPresentation.renderElement(myFixture.lookupElements[0])
|
||||
assert p.itemText == 'getValue'
|
||||
assert p.itemTextBold
|
||||
assert p.typeText == 'Foo'
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -125,7 +125,7 @@ public class GroovyNoVariantsDelegator extends CompletionContributor {
|
||||
}
|
||||
|
||||
return factory.createReferenceExpressionFromText("xxx.xxx",
|
||||
ReferenceExpressionCompletionContributor
|
||||
JavaCompletionUtil
|
||||
.createContextWithXxxVariable(place, qualifierType));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user