[java] ExternalAnnotationsManagerTest: check that typePath is actually traversable

Part of IDEA-231901 Support TYPE_USE in external annotations

GitOrigin-RevId: 971ba8142d0088bbc54ad255bee3565cef6d0a99
This commit is contained in:
Tagir Valeev
2024-09-24 11:13:21 +00:00
committed by intellij-monorepo-bot
parent 6b9761a58d
commit 4f9e77652d
@@ -103,7 +103,7 @@ public class ExternalAnnotationsManagerTest extends LightPlatformTestCase {
PsiFile psiFile = getPsiManager().findFile(file);
MostlySingularMultiMap<String, BaseExternalAnnotationsManager.AnnotationData> map = manager.getDataFromFile(psiFile);
for (String externalName : map.keySet()) {
checkExternalName(psiFile, externalName, assumedPackage);
PsiModifierListOwner listOwner = checkExternalName(psiFile, externalName, assumedPackage);
// 'annotation name="org.jetbrains.annotations.NotNull"' should have FQN
for (BaseExternalAnnotationsManager.AnnotationData annotationData : map.get(externalName)) {
@@ -113,7 +113,16 @@ public class ExternalAnnotationsManagerTest extends LightPlatformTestCase {
String typePath = annotationData.getTypePath();
if (typePath != null) {
String error = validatePath(typePath);
PsiType type;
if (listOwner instanceof PsiVariable variable) {
type = variable.getType();
} else if (listOwner instanceof PsiMethod method) {
type = method.getReturnType();
} else {
fail("typePath is not allowed for " + listOwner.getClass().getSimpleName(), psiFile, externalName);
break;
}
String error = validatePath(typePath, type);
if (error != null) {
fail("Invalid typePath: " + error, psiFile, externalName);
}
@@ -122,7 +131,7 @@ public class ExternalAnnotationsManagerTest extends LightPlatformTestCase {
}
}
private static String validatePath(String pathString) {
private static String validatePath(String pathString, PsiType type) {
if (!pathString.startsWith("/")) {
return "Must start with '/'";
}
@@ -130,12 +139,35 @@ public class ExternalAnnotationsManagerTest extends LightPlatformTestCase {
// The first component is always empty
for (int i = 1; i < components.length; i++) {
String component = components[i];
if (component.equals("[]") || component.equals("*") || component.equals(".")) {
if (component.equals("[]")) {
if (!(type instanceof PsiArrayType arrayType)) {
return "Invalid path: " + pathString + "; [] is only allowed for array types; type is "+type.getCanonicalText();
}
type = arrayType.getComponentType();
continue;
}
else if (component.equals("*")) {
if (!(type instanceof PsiWildcardType wildcardType)) {
return "Invalid path: " + pathString + "; * is only allowed for wildcard types; type is "+type.getCanonicalText();
}
type = wildcardType.getBound();
continue;
}
else if (component.equals(".")) {
// TODO: not used currently anyway
continue;
}
try {
int number = Integer.parseInt(component);
if (number >= 1 && number <= 255) {
if (!(type instanceof PsiClassType classType)) {
return "Invalid path: " + pathString + "; only class types can have components; type is "+type.getCanonicalText();
}
PsiType[] parameters = classType.getParameters();
if (number > parameters.length) {
return "Invalid path: " + pathString + "; component " + number + " is out of bounds; type is "+type.getCanonicalText();
}
type = parameters[number - 1];
continue;
}
}
@@ -171,16 +203,19 @@ public class ExternalAnnotationsManagerTest extends LightPlatformTestCase {
fail(error + "\nFile: " + psiFile.getVirtualFile().getPath() + ":" + (line+1) + " (offset: "+offset+")");
}
private void checkExternalName(@NotNull PsiFile psiFile, @NotNull String externalName, @NotNull String assumedPackage) {
private PsiModifierListOwner checkExternalName(@NotNull PsiFile psiFile, @NotNull String externalName, @NotNull String assumedPackage) {
// 'item name="java.lang.ClassLoader java.net.URL getResource(java.lang.String) 0"' should have all FQNs
String unescaped = StringUtil.unescapeXmlEntities(externalName);
List<String> words = StringUtil.split(unescaped, " ");
String className = words.get(0);
if (words.size() == 1 && JavaPsiFacade.getInstance(getProject()).findPackage(className) != null) {
return;
if (words.size() == 1) {
PsiPackage psiPackage = JavaPsiFacade.getInstance(getProject()).findPackage(className);
if (psiPackage != null) {
return psiPackage;
}
}
PsiClass aClass = assertClassFqn(className, psiFile, externalName, assumedPackage);
if (words.size() == 1) return;
if (words.size() == 1) return aClass;
String rest = unescaped.substring(className.length() + " ".length());
@@ -191,7 +226,7 @@ public class ExternalAnnotationsManagerTest extends LightPlatformTestCase {
if (psiField == null) {
fail("Field '"+field+"' not found in class '"+aClass.getQualifiedName()+"'", psiFile, externalName);
}
return;
return psiField;
}
String methodName = ContainerUtil.getLastItem(StringUtil.getWordsIn(rest.substring(0, rest.indexOf('('))));
@@ -211,25 +246,29 @@ public class ExternalAnnotationsManagerTest extends LightPlatformTestCase {
}
boolean found = !methods.isEmpty();
if (!found) {
if (KNOWN_EXCEPTIONS.contains(methodSignature)) return;
if (KNOWN_EXCEPTIONS.contains(methodSignature)) return null;
List<String> candidates = ContainerUtil.map(aClass.findMethodsByName(methodName, true), method ->
XmlUtil.escape(PsiFormatUtil.getExternalName(method, false, Integer.MAX_VALUE)));
String additionalMsg = candidates.isEmpty() ? "" : "\nMaybe you have meant one of these methods instead:\n"+StringUtil.join(candidates, "\n")+"\n";
fail("This method was not found in class '"+aClass.getQualifiedName()+"':\n"+"'"+methodSignature+"'"+additionalMsg, psiFile, externalName);
return null;
}
PsiMethod method = methods.get(0);
String parameterNumberText = StringUtil.trim(rest.substring(rest.indexOf(')') + 1));
if (parameterNumberText.isEmpty()) return;
if (parameterNumberText.isEmpty()) return method;
try {
int paramNumber = Integer.parseInt(parameterNumberText);
PsiMethod method = methods.get(0);
if (method.getParameterList().getParametersCount() <= paramNumber) {
PsiParameter parameter = method.getParameterList().getParameter(paramNumber);
if (parameter == null) {
fail("Parameter number '"+paramNumber+"' is too big for a method '"+methodSignature+"'", psiFile, externalName);
}
return parameter;
}
catch (NumberFormatException e) {
fail("Parameter number is not an integer: '"+parameterNumberText+"'", psiFile, externalName);
}
return null;
}
}