[java-rd] IDEA-333104 fix cases when highlighting get null in parents

- added new test for consistency

GitOrigin-RevId: d3aa13912b43b6717d675d5ea5a9abce7e38dad6
This commit is contained in:
Mikhail Pyltsin
2023-09-28 12:18:55 +02:00
committed by intellij-monorepo-bot
parent b851673c40
commit 8be2252d3f
2 changed files with 159 additions and 6 deletions

View File

@@ -10,6 +10,8 @@ import com.intellij.psi.tree.TokenSet;
* The BasicElementTypes interface represents a collection of basic element types used in frontback java modules.
* {@link TokenSet} is used for set, which contains `basic` types, which are not used in hierarchy.
* for other sets {@link ParentAwareTokenSet} is used.
* This set of sets must be consistent with {@link com.intellij.psi.impl.source.tree.ElementType}.
* It checks with {@link com.intellij.java.parser.FrontBackElementTypeTest#testJavaTokenSet()}
*
* @see ParentAwareTokenSet
*/

View File

@@ -7,15 +7,17 @@ import com.intellij.openapi.application.ex.PathManagerEx;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.util.text.Strings;
import com.intellij.psi.impl.source.AbstractBasicJavaDocElementTypeFactory;
import com.intellij.psi.impl.source.AbstractBasicJavaElementTypeFactory;
import com.intellij.psi.impl.source.BasicJavaDocElementType;
import com.intellij.psi.impl.source.BasicJavaElementType;
import com.intellij.psi.impl.source.*;
import com.intellij.psi.impl.source.tree.ElementType;
import com.intellij.psi.impl.source.tree.JavaDocElementType;
import com.intellij.psi.impl.source.tree.JavaDocElementTypeFactory;
import com.intellij.psi.impl.source.tree.JavaElementTypeFactory;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.ParentAwareTokenSet;
import com.intellij.psi.tree.ParentProviderElementType;
import com.intellij.psi.tree.TokenSet;
import com.intellij.util.containers.ContainerUtil;
import org.assertj.core.api.Assertions;
import org.junit.Assert;
import java.io.IOException;
@@ -23,13 +25,163 @@ import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.*;
import java.util.stream.Collectors;
public class FrontBackElementTypeTest extends AbstractBasicJavaParsingTestCase {
public static final String BASIC = "BASIC_";
public FrontBackElementTypeTest() {
super("dummy", new JavaParsingTestConfigurator());
}
public void testJavaTokenSet() throws IOException, IllegalAccessException {
checkTokenSetContains(ElementType.class, BasicJavaElementType.class);
checkTokenSet(ElementType.class, BasicElementTypes.class);
}
public void testJavaDocTokenSet() throws IllegalAccessException {
checkTokenSet(JavaDocElementType.class, BasicJavaDocElementType.class);
}
private static void checkTokenSet(Class<?> mainTokenSetClass, Class<?> basicTokenSetClass)
throws IllegalAccessException {
Map<String, TokenSet> mainTokenSets = getFieldsByName(mainTokenSetClass, TokenSet.class);
Map<String, ParentAwareTokenSet> mainParentTokenSets = getFieldsByName(mainTokenSetClass, ParentAwareTokenSet.class);
Assert.assertEquals("ParentAwareTokenSet should not be used in " + mainTokenSetClass.getName() + ", use TokenSet",
mainParentTokenSets.size(), 0);
Map<String, TokenSet> basicTokenSets = getFieldsByName(basicTokenSetClass, TokenSet.class);
Map<String, ParentAwareTokenSet> basicParentTokenSets = getFieldsByName(basicTokenSetClass, ParentAwareTokenSet.class);
IElementType[] allLoadedTypes = IElementType.enumerate(e -> true);
for (Map.Entry<String, TokenSet> basicTokenSet : basicTokenSets.entrySet()) {
ParentAwareTokenSet parentAwareTokenSet = ParentAwareTokenSet.create(basicTokenSet.getValue());
Set<IElementType> parentContained = getContained(parentAwareTokenSet, allLoadedTypes);
Set<IElementType> contained = getContained(basicTokenSet.getValue(), allLoadedTypes);
Assertions.assertThat(parentContained)
.withFailMessage("TokenSet " +
basicTokenSet.getKey() +
" probably contains ParentProviderElementType and should be converted to ParentAwareTokenSet")
.containsExactlyInAnyOrderElementsOf(contained);
}
for (Map.Entry<String, TokenSet> basicTokenSetsEntry : basicTokenSets.entrySet()) {
String basicTokenSetName = basicTokenSetsEntry.getKey();
if (!basicTokenSetName.startsWith(BASIC)) {
Assertions.fail("Name of TokenSet " + basicTokenSetName + " doesn't start with " + BASIC + " in " + basicTokenSetClass.getName());
}
String expectedBasicTokenSetName = basicTokenSetName.substring(BASIC.length());
TokenSet tokenSet = mainTokenSets.get(expectedBasicTokenSetName);
if (tokenSet == null) {
Assertions.fail(mainTokenSetClass.getName() + " doesn't contain TokenSet with name " + expectedBasicTokenSetName + ". " +
basicTokenSetClass.getName() + "contains " + basicTokenSetName);
}
if (tokenSet != basicTokenSetsEntry.getValue()) {
Assertions.fail("TokenSets with names :" + basicTokenSetName + " and " + expectedBasicTokenSetName + " from " +
mainTokenSetClass.getName() + " and " + basicTokenSetClass.getName() + " are different");
}
}
for (Map.Entry<String, ParentAwareTokenSet> basicTokenSetsEntry : basicParentTokenSets.entrySet()) {
String basicTokenSetName = basicTokenSetsEntry.getKey();
if (!basicTokenSetName.startsWith(BASIC)) {
Assertions.fail(
"Name of ParentAwareTokenSet " + basicTokenSetName + " doesn't start with " + BASIC + " in " + basicTokenSetClass.getName());
}
String expectedBasicTokenSetName = basicTokenSetName.substring(BASIC.length());
TokenSet tokenSet = mainTokenSets.get(expectedBasicTokenSetName);
if (tokenSet == null) {
Assertions.fail(mainTokenSetClass.getName() + " doesn't contain TokenSet with name " + expectedBasicTokenSetName + ". " +
basicTokenSetClass.getName() + "contains " + basicTokenSetName);
}
Set<IElementType> parentContained = getContained(basicTokenSetsEntry.getValue(), allLoadedTypes);
Set<IElementType> contained = getContained(tokenSet, allLoadedTypes);
if (!parentContained.containsAll(contained)) {
contained.removeAll(parentContained);
Assertions.fail("ParentAwareTokenSet " +
basicTokenSetName +
" doesn't contains all IElementType, which TokenSet " +
expectedBasicTokenSetName +
" contains. " +
"see " +
basicTokenSetClass.getName() +
" and " +
mainTokenSetClass.getName() +
". Difference: " +
contained.stream().map(t -> t.getDebugName()).collect(Collectors.joining(", ")));
}
Set<IElementType> additionalSet = new HashSet<>();
for (IElementType elementType : contained) {
if (elementType instanceof ParentProviderElementType parentProviderElementType) {
additionalSet.addAll(parentProviderElementType.getParents());
}
}
contained.addAll(additionalSet);
if (!contained.containsAll(parentContained)) {
parentContained.removeAll(contained);
Assertions.fail("TokenSet " +
expectedBasicTokenSetName +
" doesn't contains all IElementType, which ParentAwareTokenSet " +
basicTokenSetName +
" contains. " +
"see " +
basicTokenSetClass.getName() +
" and " +
mainTokenSetClass.getName() +
". Difference: " +
parentContained.stream().map(t -> t.getDebugName()).collect(Collectors.joining(", ")));
}
}
}
private static Set<IElementType> getContained(ParentAwareTokenSet set, IElementType[] types) {
Set<IElementType> result = new HashSet<>();
for (IElementType type : types) {
if (set.contains(type)) {
result.add(type);
}
}
return result;
}
private static Set<IElementType> getContained(TokenSet set, IElementType[] types) {
Set<IElementType> result = new HashSet<>();
for (IElementType type : types) {
if (set.contains(type)) {
result.add(type);
}
}
return result;
}
private static <T> Map<String, T> getFieldsByName(Class<?> aClass, Class<T> targetType) throws IllegalAccessException {
Field[] fields = aClass.getDeclaredFields();
HashMap<String, T> result = new HashMap<>();
for (Field field : fields) {
field.setAccessible(true);
Class<?> fieldType = field.getType();
if (fieldType == targetType) {
String name = field.getName();
Object object = field.get(null);
result.put(name, (T)object);
}
}
return result;
}
private static void checkTokenSetContains(Class<?> mainTokenSet, Class<?> elements) throws IOException {
String pathToModule = FileUtil.toSystemDependentName("/../../java-psi-impl/src/");
String fullPath = PathManagerEx.getTestDataPath() + pathToModule;
String pathToClass = mainTokenSet.getName();
checkContains(fullPath + FileUtil.toSystemDependentName(pathToClass.replace(".", "/") + ".java"), elements.getSimpleName());
}
public void testJavaElementType() {
AbstractBasicJavaElementTypeFactory.JavaElementTypeContainer javaElementTypeContainer = JavaElementTypeFactory.INSTANCE.getContainer();
check(javaElementTypeContainer);
@@ -60,7 +212,6 @@ public class FrontBackElementTypeTest extends AbstractBasicJavaParsingTestCase {
Assert.assertNotEquals(parserPath, backJavaElementTypePath, "Lexer and element types must be placed in different folders");
//not used in parser and lexer
System.out.println();
String pathToModule = FileUtil.toSystemDependentName("/../../java-frontback-psi-impl/src/");
String fullPath = PathManagerEx.getTestDataPath() + pathToModule;
checkContains(fullPath + FileUtil.toSystemDependentName(lexerPath.replace(".", "/")), BasicJavaElementType.class.getSimpleName());