IDEA-66081 (resolve methods in javadoc by signatures)

This commit is contained in:
Roman Shevchenko
2011-03-07 22:00:06 +01:00
parent 20b933ac87
commit c3b799c246
8 changed files with 259 additions and 168 deletions

View File

@@ -48,12 +48,10 @@ import java.util.*;
public class JavaDocReferenceInspection extends BaseLocalInspectionTool { public class JavaDocReferenceInspection extends BaseLocalInspectionTool {
@NonNls public static final String SHORT_NAME = "JavadocReference"; @NonNls public static final String SHORT_NAME = "JavadocReference";
public static final String DISPLAY_NAME = InspectionsBundle.message("inspection.javadoc.ref.display.name");
private static ProblemDescriptor createDescriptor(@NotNull PsiElement element, String template, InspectionManager manager, private static ProblemDescriptor createDescriptor(@NotNull PsiElement element, String template, InspectionManager manager,
boolean onTheFly) { boolean onTheFly) {
return manager.createProblemDescriptor(element, template, onTheFly, (LocalQuickFix [])null, ProblemHighlightType.LIKE_UNKNOWN_SYMBOL); return manager.createProblemDescriptor(element, template, onTheFly, null, ProblemHighlightType.LIKE_UNKNOWN_SYMBOL);
} }
@Nullable @Nullable
@@ -66,9 +64,14 @@ public class JavaDocReferenceInspection extends BaseLocalInspectionTool {
return checkMember(field, manager, isOnTheFly); return checkMember(field, manager, isOnTheFly);
} }
@Nullable
public ProblemDescriptor[] checkClass(@NotNull PsiClass aClass, @NotNull InspectionManager manager, boolean isOnTheFly) {
return checkMember(aClass, manager, isOnTheFly);
}
@Nullable @Nullable
private ProblemDescriptor[] checkMember(final PsiDocCommentOwner docCommentOwner, final InspectionManager manager, final boolean isOnTheFly) { private ProblemDescriptor[] checkMember(final PsiDocCommentOwner docCommentOwner, final InspectionManager manager, final boolean isOnTheFly) {
ArrayList<ProblemDescriptor> problems = new ArrayList<ProblemDescriptor>(); final ArrayList<ProblemDescriptor> problems = new ArrayList<ProblemDescriptor>();
final PsiDocComment docComment = docCommentOwner.getDocComment(); final PsiDocComment docComment = docCommentOwner.getDocComment();
if (docComment == null) return null; if (docComment == null) return null;
@@ -77,26 +80,20 @@ public class JavaDocReferenceInspection extends BaseLocalInspectionTool {
for (PsiJavaCodeReferenceElement reference : references) { for (PsiJavaCodeReferenceElement reference : references) {
final List<PsiClass> classesToImport = new ImportClassFix(reference).getClassesToImport(); final List<PsiClass> classesToImport = new ImportClassFix(reference).getClassesToImport();
final PsiElement referenceNameElement = reference.getReferenceNameElement(); final PsiElement referenceNameElement = reference.getReferenceNameElement();
problems.add(manager.createProblemDescriptor(referenceNameElement != null ? referenceNameElement : reference, cannotResolveSymbolMessage("<code>" + reference.getText() + "</code>"), problems.add(manager.createProblemDescriptor(referenceNameElement != null ? referenceNameElement : reference,
!isOnTheFly || classesToImport.isEmpty() ? null : new AddImportFix(classesToImport), ProblemHighlightType.LIKE_UNKNOWN_SYMBOL, cannotResolveSymbolMessage("<code>" + reference.getText() + "</code>"),
isOnTheFly)); !isOnTheFly || classesToImport.isEmpty() ? null : new AddImportFix(classesToImport),
ProblemHighlightType.LIKE_UNKNOWN_SYMBOL, isOnTheFly));
} }
return problems.isEmpty() return problems.isEmpty() ? null : problems.toArray(new ProblemDescriptor[problems.size()]);
? null
: problems.toArray(new ProblemDescriptor[problems.size()]);
} }
@Nullable
public ProblemDescriptor[] checkClass(@NotNull PsiClass aClass, @NotNull InspectionManager manager, boolean isOnTheFly) {
return checkMember(aClass, manager, isOnTheFly);
}
private PsiElementVisitor getVisitor(final Set<PsiJavaCodeReferenceElement> references, private PsiElementVisitor getVisitor(final Set<PsiJavaCodeReferenceElement> references,
final PsiElement context, final PsiElement context,
final ArrayList<ProblemDescriptor> problems, final ArrayList<ProblemDescriptor> problems,
final InspectionManager manager, final boolean onTheFly) { final InspectionManager manager,
final boolean onTheFly) {
return new JavaElementVisitor() { return new JavaElementVisitor() {
@Override public void visitReferenceExpression(PsiReferenceExpression expression) { @Override public void visitReferenceExpression(PsiReferenceExpression expression) {
visitElement(expression); visitElement(expression);
@@ -137,58 +134,56 @@ public class JavaDocReferenceInspection extends BaseLocalInspectionTool {
}; };
} }
public static void visitRefInDocTag(final PsiDocTag tag, final JavadocManager manager, final PsiElement context, ArrayList<ProblemDescriptor> problems, public static void visitRefInDocTag(final PsiDocTag tag,
InspectionManager inspectionManager, final JavadocManager manager,
boolean onTheFly) { final PsiElement context,
final ArrayList<ProblemDescriptor> problems,
final InspectionManager inspectionManager,
final boolean onTheFly) {
final String tagName = tag.getName(); final String tagName = tag.getName();
PsiDocTagValue value = tag.getValueElement(); final PsiDocTagValue value = tag.getValueElement();
if (value == null) return; if (value == null) return;
final JavadocTagInfo info = manager.getTagInfo(tagName); final JavadocTagInfo info = manager.getTagInfo(tagName);
if (info != null && !info.isValidInContext(context)) return; if (info != null && !info.isValidInContext(context)) return;
String message = info == null || !info.isInline() ? null : info.checkTagValue(value); final String message = info == null || !info.isInline() ? null : info.checkTagValue(value);
if (message != null){ if (message != null){
problems.add(createDescriptor(value, message, inspectionManager, onTheFly)); problems.add(createDescriptor(value, message, inspectionManager, onTheFly));
} }
final PsiReference reference = value.getReference(); final PsiReference reference = value.getReference();
if (reference != null) { if (reference == null) return;
PsiElement element = reference.resolve(); final PsiElement element = reference.resolve();
if (element == null) { if (element != null) return;
final int textOffset = value.getTextOffset(); final int textOffset = value.getTextOffset();
if (textOffset == value.getTextRange().getEndOffset()) return;
final PsiDocTagValue valueElement = tag.getValueElement();
if (valueElement == null) return;
if (textOffset != value.getTextRange().getEndOffset()) { final CharSequence paramName = value.getContainingFile().getViewProvider().getContents().subSequence(textOffset, value.getTextRange().getEndOffset());
final PsiDocTagValue valueElement = tag.getValueElement(); final String params = "<code>" + paramName + "</code>";
if (valueElement != null) { final List<LocalQuickFix> fixes = new ArrayList<LocalQuickFix>();
final CharSequence paramName = if (onTheFly && "param".equals(tagName)) {
value.getContainingFile().getViewProvider().getContents().subSequence(textOffset, value.getTextRange().getEndOffset()); final PsiDocCommentOwner commentOwner = PsiTreeUtil.getParentOfType(tag, PsiDocCommentOwner.class);
@NonNls String params = "<code>" + paramName + "</code>"; if (commentOwner instanceof PsiMethod) {
final PsiMethod method = (PsiMethod)commentOwner;
final List<LocalQuickFix> fixes = new ArrayList<LocalQuickFix>(); final PsiParameter[] parameters = method.getParameterList().getParameters();
if (onTheFly && "param".equals(tagName)) { final PsiDocTag[] tags = tag.getContainingComment().getTags();
final PsiDocCommentOwner commentOwner = PsiTreeUtil.getParentOfType(tag, PsiDocCommentOwner.class); final Set<String> unboundParams = new HashSet<String>();
if (commentOwner instanceof PsiMethod) { for (PsiParameter parameter : parameters) {
final PsiMethod method = (PsiMethod)commentOwner; if (!JavaDocLocalInspection.isFound(tags, parameter)) {
final PsiParameter[] parameters = method.getParameterList().getParameters(); unboundParams.add(parameter.getName());
final PsiDocTag[] tags = tag.getContainingComment().getTags();
final Set<String> unboundParams = new HashSet<String>();
for (PsiParameter parameter : parameters) {
if (!JavaDocLocalInspection.isFound(tags, parameter)) {
unboundParams.add(parameter.getName());
}
}
if (!unboundParams.isEmpty()) {
fixes.add(new RenameReferenceQuickFix(unboundParams));
}
}
}
fixes.add(new RemoveTagFix(tagName, paramName, tag));
problems.add(inspectionManager.createProblemDescriptor(valueElement, reference.getRangeInElement(), cannotResolveSymbolMessage(params),
ProblemHighlightType.LIKE_UNKNOWN_SYMBOL, onTheFly,
fixes.toArray(new LocalQuickFix[fixes.size()])));
} }
} }
if (!unboundParams.isEmpty()) {
fixes.add(new RenameReferenceQuickFix(unboundParams));
}
} }
} }
fixes.add(new RemoveTagFix(tagName, paramName, tag));
problems.add(inspectionManager.createProblemDescriptor(valueElement, reference.getRangeInElement(), cannotResolveSymbolMessage(params),
ProblemHighlightType.LIKE_UNKNOWN_SYMBOL, onTheFly,
fixes.toArray(new LocalQuickFix[fixes.size()])));
} }
private static String cannotResolveSymbolMessage(String params) { private static String cannotResolveSymbolMessage(String params) {
@@ -197,7 +192,7 @@ public class JavaDocReferenceInspection extends BaseLocalInspectionTool {
@NotNull @NotNull
public String getDisplayName() { public String getDisplayName() {
return DISPLAY_NAME; return InspectionsBundle.message("inspection.javadoc.ref.display.name");
} }
@NotNull @NotNull

View File

@@ -15,8 +15,8 @@
*/ */
package com.intellij.psi.impl.source.javadoc; package com.intellij.psi.impl.source.javadoc;
import com.google.common.collect.Lists;
import com.intellij.lang.ASTNode; import com.intellij.lang.ASTNode;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*; import com.intellij.psi.*;
import com.intellij.psi.impl.source.Constants; import com.intellij.psi.impl.source.Constants;
@@ -29,9 +29,9 @@ import com.intellij.psi.javadoc.PsiDocTagValue;
import com.intellij.psi.scope.ElementClassFilter; import com.intellij.psi.scope.ElementClassFilter;
import com.intellij.psi.scope.PsiScopeProcessor; import com.intellij.psi.scope.PsiScopeProcessor;
import com.intellij.psi.scope.processor.FilterScopeProcessor; import com.intellij.psi.scope.processor.FilterScopeProcessor;
import com.intellij.psi.util.MethodSignature;
import com.intellij.psi.util.MethodSignatureUtil;
import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.util.ArrayUtil; import com.intellij.util.ArrayUtil;
import com.intellij.util.CharTable; import com.intellij.util.CharTable;
import com.intellij.util.IncorrectOperationException; import com.intellij.util.IncorrectOperationException;
@@ -85,8 +85,6 @@ public class PsiDocMethodOrFieldRef extends CompositePsiElement implements PsiDo
@Nullable @Nullable
private PsiReference getReferenceInScope(PsiElement scope, PsiElement element) { private PsiReference getReferenceInScope(PsiElement scope, PsiElement element) {
final String name = element.getText(); final String name = element.getText();
final String[] signature = getSignature(); final String[] signature = getSignature();
if (signature == null) { if (signature == null) {
@@ -97,49 +95,44 @@ public class PsiDocMethodOrFieldRef extends CompositePsiElement implements PsiDo
} }
} }
final PsiMethod[] methods = getAllMethods(scope, this); final MethodSignature methodSignature;
if (signature != null) {
nextMethod: final List<PsiType> types = Lists.newArrayListWithCapacity(signature.length);
for (PsiMethod method : methods) { final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(element.getProject()).getElementFactory();
if (!method.getName().equals(name)) continue; for (String s : signature) {
try {
if (signature == null) { types.add(elementFactory.createTypeFromText(s, scope));
return new MyReference(method); }
} catch (IncorrectOperationException e) {
else { types.add(PsiType.NULL);
final PsiParameter[] parameters = method.getParameterList().getParameters();
if (parameters.length != signature.length) continue;
for (int j = 0; j < parameters.length; j++) {
PsiParameter parameter = parameters[j];
PsiType type1 = TypeConversionUtil.erasure(parameter.getType());
String type2 = signature[j];
if (!Comparing.strEqual(type1.getPresentableText(), type2) && !Comparing.strEqual(type1.getCanonicalText(), type2)) {
String shortName = "";
PsiClass psiClass = PsiUtil.resolveClassInType(type1);
while (psiClass != null) {
shortName = psiClass.getName() + (shortName.length() > 0 ? "." + shortName : "");
psiClass = PsiTreeUtil.getParentOfType(psiClass, PsiClass.class);
}
if (!Comparing.strEqual(shortName, type2)) {
continue nextMethod;
}
}
} }
return new MyReference(method) {
@NotNull
public PsiElement[] getVariants() {
final List<PsiMethod> lst = new ArrayList<PsiMethod>();
for (PsiMethod method : methods) {
if (name.equals(method.getName())) {
lst.add(method);
}
}
return lst.toArray(new PsiMethod[lst.size()]);
}
};
} }
methodSignature = MethodSignatureUtil.createMethodSignature(name, types.toArray(new PsiType[types.size()]),
PsiTypeParameter.EMPTY_ARRAY, PsiSubstitutor.EMPTY);
} }
else {
methodSignature = MethodSignatureUtil.createMethodSignature(name, PsiType.EMPTY_ARRAY,
PsiTypeParameter.EMPTY_ARRAY, PsiSubstitutor.EMPTY);
}
final PsiMethod[] methods = getAllMethods(scope, this);
for (PsiMethod method : methods) {
if (!method.getName().equals(name) ||
!MethodSignatureUtil.areSignaturesErasureEqual(methodSignature, method.getSignature(PsiSubstitutor.EMPTY))) continue;
return new MyReference(method) {
@NotNull
public PsiElement[] getVariants() {
final List<PsiMethod> lst = new ArrayList<PsiMethod>();
for (PsiMethod method : methods) {
if (name.equals(method.getName())) {
lst.add(method);
}
}
return lst.toArray(new PsiMethod[lst.size()]);
}
};
}
return null; return null;
} }
@@ -157,34 +150,30 @@ public class PsiDocMethodOrFieldRef extends CompositePsiElement implements PsiDo
public int getTextOffset() { public int getTextOffset() {
final PsiElement element = getNameElement(); final PsiElement element = getNameElement();
return element != null ? element.getTextRange().getStartOffset() : getTextRange().getEndOffset();
if (element != null) {
return element.getTextRange().getStartOffset();
}
return getTextRange().getEndOffset();
} }
@Nullable
public PsiElement getNameElement() { public PsiElement getNameElement() {
final ASTNode sharp = findChildByType(DOC_TAG_VALUE_SHARP_TOKEN); final ASTNode sharp = findChildByType(DOC_TAG_VALUE_SHARP_TOKEN);
if (sharp == null) return null; return sharp != null ? SourceTreeToPsiMap.treeToPsiNotNull(sharp).getNextSibling() : null;
return SourceTreeToPsiMap.treeElementToPsi(sharp).getNextSibling();
} }
@Nullable
public String[] getSignature() { public String[] getSignature() {
PsiElement element = getNameElement().getNextSibling(); PsiElement element = getNameElement();
if (element == null) return null;
element = element.getNextSibling();
while (element != null && !(element instanceof PsiDocTagValue)) { while (element != null && !(element instanceof PsiDocTagValue)) {
element = element.getNextSibling(); element = element.getNextSibling();
} }
if (element == null) return null; if (element == null) return null;
List<String> types = new ArrayList<String>(); List<String> types = new ArrayList<String>();
for (PsiElement child = element.getFirstChild(); child != null; child = child.getNextSibling()) { for (PsiElement child = element.getFirstChild(); child != null; child = child.getNextSibling()) {
if (child.getNode().getElementType() == DOC_TYPE_HOLDER) { if (child.getNode().getElementType() == DOC_TYPE_HOLDER) {
final String[] typeStrings = child.getText().split("[, ]"); //avoid param types list parsing hmm mathod(paramType1, paramType2, ...) -> typeElement1, identifier2, ... final String[] typeStrings = child.getText().split("[, ]"); //avoid param types list parsing hmm method(paramType1, paramType2, ...) -> typeElement1, identifier2, ...
if (typeStrings != null) { if (typeStrings != null) {
for (String type : typeStrings) { for (String type : typeStrings) {
if (type.length() > 0) { if (type.length() > 0) {
@@ -224,14 +213,14 @@ public class PsiDocMethodOrFieldRef extends CompositePsiElement implements PsiDo
} }
public class MyReference implements PsiJavaReference { public class MyReference implements PsiJavaReference {
private final PsiElement myReferencee; private final PsiElement myReferredElement;
public MyReference(PsiElement referencee) { public MyReference(PsiElement referredElement) {
myReferencee = referencee; myReferredElement = referredElement;
} }
public PsiElement resolve() { public PsiElement resolve() {
return myReferencee; return myReferredElement;
} }
public void processVariants(PsiScopeProcessor processor) { public void processVariants(PsiScopeProcessor processor) {
@@ -244,12 +233,14 @@ public class PsiDocMethodOrFieldRef extends CompositePsiElement implements PsiDo
@NotNull @NotNull
public JavaResolveResult advancedResolve(boolean incompleteCode) { public JavaResolveResult advancedResolve(boolean incompleteCode) {
return myReferencee == null ? JavaResolveResult.EMPTY : new CandidateInfo(myReferencee, PsiSubstitutor.EMPTY); return myReferredElement == null ? JavaResolveResult.EMPTY
: new CandidateInfo(myReferredElement, PsiSubstitutor.EMPTY);
} }
@NotNull @NotNull
public JavaResolveResult[] multiResolve(boolean incompleteCode) { public JavaResolveResult[] multiResolve(boolean incompleteCode) {
return myReferencee == null ? JavaResolveResult.EMPTY_ARRAY : new JavaResolveResult[]{new CandidateInfo(myReferencee, PsiSubstitutor.EMPTY)}; return myReferredElement == null ? JavaResolveResult.EMPTY_ARRAY
: new JavaResolveResult[]{new CandidateInfo(myReferredElement, PsiSubstitutor.EMPTY)};
} }
@NotNull @NotNull
@@ -269,21 +260,26 @@ public class PsiDocMethodOrFieldRef extends CompositePsiElement implements PsiDo
@NotNull @NotNull
public String getCanonicalText() { public String getCanonicalText() {
return getNameElement().getText(); final PsiElement nameElement = getNameElement();
assert nameElement != null;
return nameElement.getText();
} }
public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException { public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException {
final PsiElement element = getNameElement(); final PsiElement nameElement = getNameElement();
final ASTNode treeElement = SourceTreeToPsiMap.psiElementToTree(element); assert nameElement != null;
final ASTNode treeElement = SourceTreeToPsiMap.psiToTreeNotNull(nameElement);
final CharTable charTableByTree = SharedImplUtil.findCharTableByTree(treeElement); final CharTable charTableByTree = SharedImplUtil.findCharTableByTree(treeElement);
LeafElement newToken = Factory.createSingleLeafElement(DOC_TAG_VALUE_TOKEN, newElementName, charTableByTree, getManager()); final LeafElement newToken = Factory.createSingleLeafElement(DOC_TAG_VALUE_TOKEN, newElementName, charTableByTree, getManager());
((CompositeElement)treeElement.getTreeParent()).replaceChildInternal(SourceTreeToPsiMap.psiElementToTree(element), newToken); ((CompositeElement)treeElement.getTreeParent()).replaceChildInternal(SourceTreeToPsiMap.psiToTreeNotNull(nameElement), newToken);
return SourceTreeToPsiMap.treeElementToPsi(newToken); return SourceTreeToPsiMap.treeToPsiNotNull(newToken);
} }
public PsiElement bindToElement(@NotNull PsiElement element) throws IncorrectOperationException { public PsiElement bindToElement(@NotNull PsiElement element) throws IncorrectOperationException {
if (isReferenceTo(element)) return PsiDocMethodOrFieldRef.this; if (isReferenceTo(element)) return PsiDocMethodOrFieldRef.this;
final String name = getNameElement().getText(); final PsiElement nameElement = getNameElement();
assert nameElement != null;
final String name = nameElement.getText();
final String newName; final String newName;
final PsiMethod method; final PsiMethod method;
@@ -305,15 +301,16 @@ public class PsiDocMethodOrFieldRef extends CompositePsiElement implements PsiDo
throw new IncorrectOperationException(); throw new IncorrectOperationException();
} }
final PsiElement child = getFirstChild();
if (getFirstChild().getNode().getElementType() == ElementType.DOC_REFERENCE_HOLDER) { if (containingClass != null && child != null && child.getNode().getElementType() == ElementType.DOC_REFERENCE_HOLDER) {
PsiJavaCodeReferenceElement referenceElement = (PsiJavaCodeReferenceElement) getFirstChild().getFirstChild(); final PsiJavaCodeReferenceElement referenceElement = (PsiJavaCodeReferenceElement) child.getFirstChild();
assert referenceElement != null;
referenceElement.bindToElement(containingClass); referenceElement.bindToElement(containingClass);
} }
else { else {
if (!PsiTreeUtil.isAncestor(containingClass, PsiDocMethodOrFieldRef.this, true)) { if (containingClass != null && !PsiTreeUtil.isAncestor(containingClass, PsiDocMethodOrFieldRef.this, true)) {
final PsiReferenceExpression ref = final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(containingClass.getProject()).getElementFactory();
JavaPsiFacade.getInstance(containingClass.getProject()).getElementFactory().createReferenceExpression(containingClass); final PsiReferenceExpression ref = elementFactory.createReferenceExpression(containingClass);
addAfter(ref, null); addAfter(ref, null);
} }
} }
@@ -352,10 +349,11 @@ public class PsiDocMethodOrFieldRef extends CompositePsiElement implements PsiDo
} }
public PsiElement bindToText(PsiClass containingClass, StringBuffer newText) { public PsiElement bindToText(PsiClass containingClass, StringBuffer newText) {
PsiComment comment = PsiElementFactory elementFactory = JavaPsiFacade.getInstance(containingClass.getProject()).getElementFactory();
JavaPsiFacade.getInstance(containingClass.getProject()).getElementFactory().createCommentFromText(newText.toString(), null); PsiComment comment = elementFactory.createCommentFromText(newText.toString(), null);
PsiElement tag = PsiTreeUtil.getChildOfType(comment, PsiDocTag.class); PsiElement tag = PsiTreeUtil.getChildOfType(comment, PsiDocTag.class);
PsiElement ref = PsiTreeUtil.getChildOfType(tag, PsiDocMethodOrFieldRef.class); PsiElement ref = PsiTreeUtil.getChildOfType(tag, PsiDocMethodOrFieldRef.class);
assert ref != null : newText;
return replace(ref); return replace(ref);
} }
@@ -366,20 +364,20 @@ public class PsiDocMethodOrFieldRef extends CompositePsiElement implements PsiDo
public TextRange getRangeInElement() { public TextRange getRangeInElement() {
final ASTNode sharp = findChildByType(DOC_TAG_VALUE_SHARP_TOKEN); final ASTNode sharp = findChildByType(DOC_TAG_VALUE_SHARP_TOKEN);
if (sharp == null) return new TextRange(0, getTextLength()); if (sharp == null) return new TextRange(0, getTextLength());
final PsiElement nextSibling = SourceTreeToPsiMap.treeElementToPsi(sharp).getNextSibling(); final PsiElement nextSibling = SourceTreeToPsiMap.treeToPsiNotNull(sharp).getNextSibling();
if(nextSibling != null){ if (nextSibling != null) {
final int startOffset = nextSibling.getTextRange().getStartOffset() - getTextRange().getStartOffset(); final int startOffset = nextSibling.getTextRange().getStartOffset() - getTextRange().getStartOffset();
int endOffset = nextSibling.getTextRange().getEndOffset() - getTextRange().getStartOffset(); int endOffset = nextSibling.getTextRange().getEndOffset() - getTextRange().getStartOffset();
final PsiElement nextParSibling = nextSibling.getNextSibling(); final PsiElement nextParSibling = nextSibling.getNextSibling();
if(nextParSibling != null && "(".equals(nextParSibling.getText())){ if (nextParSibling != null && "(".equals(nextParSibling.getText())) {
endOffset ++; endOffset++;
PsiElement nextElement = nextParSibling.getNextSibling(); PsiElement nextElement = nextParSibling.getNextSibling();
if(nextElement != null && SourceTreeToPsiMap.psiElementToTree(nextElement).getElementType() == DOC_TAG_VALUE_TOKEN){ if (nextElement != null && SourceTreeToPsiMap.psiToTreeNotNull(nextElement).getElementType() == DOC_TAG_VALUE_TOKEN) {
endOffset += nextElement.getTextLength(); endOffset += nextElement.getTextLength();
nextElement = nextElement.getNextSibling(); nextElement = nextElement.getNextSibling();
} }
if(nextElement != null && ")".equals(nextElement.getText())){ if (nextElement != null && ")".equals(nextElement.getText())) {
endOffset ++; endOffset++;
} }
} }
return new TextRange(startOffset, endOffset); return new TextRange(startOffset, endOffset);

View File

@@ -0,0 +1,15 @@
package pkg;
/**
* @see #method(pkg.B0.C[])
* @see #method(B0.C[])
* @see #method(B0.C...)
*/
class A0 {
public void method(B0.C[] c) { }
}
class B0 {
class C {
}
}

View File

@@ -0,0 +1,16 @@
package pkg;
/**
* @see #<error descr="Cannot resolve symbol 'method(B1.C)'">method(B1.C)</error>
* @see #<error descr="Cannot resolve symbol 'method(B1.C[][])'">method(B1.C[][])</error>
* @see #<error descr="Cannot resolve symbol 'method(B1.C..)'">method(<error descr="Cannot resolve symbol 'B1.C.'">B1.C.</error>.)</error>
* @see #<error descr="Cannot resolve symbol 'method(B1.C[)'">method(B1.C[)</error>
*/
class A1 {
public void method(B1.C[] c) { }
}
class B1 {
class C {
}
}

View File

@@ -0,0 +1,13 @@
package pkg;
import java.util.List;
/**
* @see #method(java.util.List)
* @see #method(java.util.List<String>)
* @see #method(java.util.List<T>)
* @see #method(java.util.List<Number>)
*/
class A2<T> {
public void method(List<String> list) { }
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2000-2011 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 com.intellij.codeInsight.daemon;
import com.intellij.codeInspection.LocalInspectionTool;
import com.intellij.codeInspection.javaDoc.JavaDocLocalInspection;
import com.intellij.codeInspection.javaDoc.JavaDocReferenceInspection;
public class JavadocResolveTest extends DaemonAnalyzerTestCase {
private static final String BASE_PATH = "/codeInsight/daemonCodeAnalyzer/javaDoc/resolve";
@Override
protected LocalInspectionTool[] configureLocalInspectionTools() {
return new LocalInspectionTool[]{new JavaDocLocalInspection(), new JavaDocReferenceInspection()};
}
public void testSee0() throws Exception { doTest(); }
public void testSee1() throws Exception { doTest(); }
public void testSee2() throws Exception { doTest(); }
private void doTest() throws Exception {
doTest(BASE_PATH + "/pkg/" + getTestName(false) + ".java", BASE_PATH, false, false);
}
}

View File

@@ -23,16 +23,23 @@ import org.jetbrains.annotations.NotNull;
/** /**
* @author cdr * @author cdr
*/ */
public interface MethodSignature { public interface MethodSignature {
MethodSignature[] EMPTY_ARRAY = new MethodSignature[0]; MethodSignature[] EMPTY_ARRAY = new MethodSignature[0];
@NotNull PsiSubstitutor getSubstitutor();
@NotNull String getName();
/**
* already substituted
*/
@NotNull PsiType[] getParameterTypes();
@NotNull PsiTypeParameter[] getTypeParameters();
boolean isRaw();
@NotNull
PsiSubstitutor getSubstitutor();
@NotNull
String getName();
/**
* @return array of parameter types (already substituted)
*/
@NotNull
PsiType[] getParameterTypes();
@NotNull
PsiTypeParameter[] getTypeParameters();
boolean isRaw();
} }

View File

@@ -29,6 +29,8 @@ import java.util.Set;
public class MethodSignatureUtil { public class MethodSignatureUtil {
private MethodSignatureUtil() { }
public static final TObjectHashingStrategy<MethodSignatureBackedByPsiMethod> METHOD_BASED_HASHING_STRATEGY = public static final TObjectHashingStrategy<MethodSignatureBackedByPsiMethod> METHOD_BASED_HASHING_STRATEGY =
new TObjectHashingStrategy<MethodSignatureBackedByPsiMethod>() { new TObjectHashingStrategy<MethodSignatureBackedByPsiMethod>() {
public int computeHashCode(final MethodSignatureBackedByPsiMethod signature) { public int computeHashCode(final MethodSignatureBackedByPsiMethod signature) {
@@ -40,9 +42,6 @@ public class MethodSignatureUtil {
} }
}; };
private MethodSignatureUtil() {
}
public static MethodSignature createMethodSignature(@NonNls @NotNull String name, public static MethodSignature createMethodSignature(@NonNls @NotNull String name,
@Nullable PsiParameterList parameterTypes, @Nullable PsiParameterList parameterTypes,
@Nullable PsiTypeParameterList typeParameterList, @Nullable PsiTypeParameterList typeParameterList,
@@ -154,10 +153,12 @@ public class MethodSignatureUtil {
return null; return null;
} }
@Nullable
public static PsiMethod findMethodBySignature(final PsiClass aClass, PsiMethod pattenMethod, boolean checkBases) { public static PsiMethod findMethodBySignature(final PsiClass aClass, PsiMethod pattenMethod, boolean checkBases) {
return findMethodBySignature(aClass, pattenMethod.getSignature(PsiSubstitutor.EMPTY), checkBases); return findMethodBySignature(aClass, pattenMethod.getSignature(PsiSubstitutor.EMPTY), checkBases);
} }
@Nullable
public static PsiMethod findMethodBySignature(final PsiClass aClass, MethodSignature methodSignature, boolean checkBases) { public static PsiMethod findMethodBySignature(final PsiClass aClass, MethodSignature methodSignature, boolean checkBases) {
List<Pair<PsiMethod, PsiSubstitutor>> pairs = aClass.findMethodsAndTheirSubstitutorsByName(methodSignature.getName(), checkBases); List<Pair<PsiMethod, PsiSubstitutor>> pairs = aClass.findMethodsAndTheirSubstitutorsByName(methodSignature.getName(), checkBases);
for (Pair<PsiMethod, PsiSubstitutor> pair : pairs) { for (Pair<PsiMethod, PsiSubstitutor> pair : pairs) {
@@ -181,13 +182,17 @@ public class MethodSignatureUtil {
return null; return null;
} }
@Nullable
public static PsiMethod findMethodBySuperMethod(final PsiClass aClass, PsiMethod method, final boolean checkBases) { public static PsiMethod findMethodBySuperMethod(final PsiClass aClass, PsiMethod method, final boolean checkBases) {
List<Pair<PsiMethod, PsiSubstitutor>> pairs = aClass.findMethodsAndTheirSubstitutorsByName(method.getName(), checkBases); List<Pair<PsiMethod, PsiSubstitutor>> pairs = aClass.findMethodsAndTheirSubstitutorsByName(method.getName(), checkBases);
for (Pair<PsiMethod, PsiSubstitutor> pair : pairs) { for (Pair<PsiMethod, PsiSubstitutor> pair : pairs) {
PsiMethod candidate = pair.first; PsiMethod candidate = pair.first;
PsiSubstitutor substitutor = pair.second; PsiSubstitutor substitutor = pair.second;
MethodSignature candidateSignature = candidate.getSignature(substitutor); MethodSignature candidateSignature = candidate.getSignature(substitutor);
PsiSubstitutor superSubstitutor = TypeConversionUtil.getClassSubstitutor(method.getContainingClass(), candidate.getContainingClass(), substitutor); final PsiClass methodClass = method.getContainingClass();
final PsiClass candidateClass = candidate.getContainingClass();
if (methodClass == null || candidateClass == null) continue;
PsiSubstitutor superSubstitutor = TypeConversionUtil.getClassSubstitutor(methodClass, candidateClass, substitutor);
if (superSubstitutor == null) continue; if (superSubstitutor == null) continue;
MethodSignature superSignature = method.getSignature(superSubstitutor); MethodSignature superSignature = method.getSignature(superSubstitutor);
if (isSubsignature(superSignature, candidateSignature)) return candidate; if (isSubsignature(superSignature, candidateSignature)) return candidate;
@@ -206,41 +211,47 @@ public class MethodSignatureUtil {
} }
public static boolean areParametersErasureEqual(PsiMethod method1, PsiMethod method2) { public static boolean areParametersErasureEqual(PsiMethod method1, PsiMethod method2) {
return METHOD_PARAMETERS_ERASURE_EQUALITY.equals(method1.getSignature(PsiSubstitutor.EMPTY), return areSignaturesErasureEqual(method1.getSignature(PsiSubstitutor.EMPTY), method2.getSignature(PsiSubstitutor.EMPTY));
method2.getSignature(PsiSubstitutor.EMPTY)); }
public static boolean areSignaturesErasureEqual(MethodSignature signature1, MethodSignature signature2) {
return METHOD_PARAMETERS_ERASURE_EQUALITY.equals(signature1, signature2);
} }
/** /**
* @param methodSignature * @param methodSignature method signature
* @param superMethodSignature * @param superMethodSignature super method signature
* @return null if signatures do not match * @return null if signatures do not match
*/ */
@Nullable
public static PsiSubstitutor getSuperMethodSignatureSubstitutor(MethodSignature methodSignature, MethodSignature superMethodSignature) { public static PsiSubstitutor getSuperMethodSignatureSubstitutor(MethodSignature methodSignature, MethodSignature superMethodSignature) {
PsiSubstitutor result = getSuperMethodSignatureSubstitutorImpl(methodSignature, superMethodSignature); PsiSubstitutor result = getSuperMethodSignatureSubstitutorImpl(methodSignature, superMethodSignature);
if (result == null) return null; if (result == null) return null;
PsiTypeParameter[] methoTypeParameters = methodSignature.getTypeParameters(); PsiTypeParameter[] methodTypeParameters = methodSignature.getTypeParameters();
PsiTypeParameter[] superTypeParameters = superMethodSignature.getTypeParameters(); PsiTypeParameter[] superTypeParameters = superMethodSignature.getTypeParameters();
PsiSubstitutor methodSubstitutor = methodSignature.getSubstitutor(); PsiSubstitutor methodSubstitutor = methodSignature.getSubstitutor();
//check bounds //check bounds
for (int i = 0; i < methoTypeParameters.length; i++) { for (int i = 0; i < methodTypeParameters.length; i++) {
PsiTypeParameter methoTypeParameter = methoTypeParameters[i]; PsiTypeParameter methodTypeParameter = methodTypeParameters[i];
PsiTypeParameter superTypeParameter = superTypeParameters[i]; PsiTypeParameter superTypeParameter = superTypeParameters[i];
final Set<PsiType> methoSupers = new HashSet<PsiType>(); final Set<PsiType> methodSupers = new HashSet<PsiType>();
for (PsiClassType methoSuper : methoTypeParameter.getSuperTypes()) { for (PsiClassType methodSuper : methodTypeParameter.getSuperTypes()) {
methoSupers.add(methodSubstitutor.substitute(methoSuper)); methodSupers.add(methodSubstitutor.substitute(methodSuper));
} }
final Set<PsiType> superSupers = new HashSet<PsiType>(); final Set<PsiType> superSupers = new HashSet<PsiType>();
for (PsiClassType superSuper : superTypeParameter.getSuperTypes()) { for (PsiClassType superSuper : superTypeParameter.getSuperTypes()) {
superSupers.add(methodSubstitutor.substitute(PsiUtil.captureToplevelWildcards(result.substitute(superSuper), methoTypeParameter))); superSupers.add(methodSubstitutor.substitute(PsiUtil.captureToplevelWildcards(result.substitute(superSuper), methodTypeParameter)));
} }
if (!methoSupers.equals(superSupers)) return null; if (!methodSupers.equals(superSupers)) return null;
} }
return result; return result;
} }
@Nullable
private static PsiSubstitutor getSuperMethodSignatureSubstitutorImpl(MethodSignature methodSignature, MethodSignature superSignature) { private static PsiSubstitutor getSuperMethodSignatureSubstitutorImpl(MethodSignature methodSignature, MethodSignature superSignature) {
// normalize generic method declarations: correlate type parameters // normalize generic method declarations: correlate type parameters
// todo: correlate type params by name? // todo: correlate type params by name?
@@ -336,5 +347,4 @@ public class MethodSignatureUtil {
} }
return true; return true;
} }
} }