[java] Basic support for external type annotations

Only in XML directly; no editing UI for now; no inlays for now; only for libraries (Cls), currently no intent to extend to sources (Psi)
Part of IDEA-231901 Support TYPE_USE in external annotations

GitOrigin-RevId: 672ed09f57ffc40b61e8fe4dd33d0f9acdac92dc
This commit is contained in:
Tagir Valeev
2024-09-18 18:47:00 +02:00
committed by intellij-monorepo-bot
parent 1c8240ecd6
commit b50767f679
20 changed files with 824 additions and 506 deletions

View File

@@ -11,10 +11,7 @@ import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiFileEx;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.PsiFormatUtil;
import com.intellij.psi.util.PsiFormatUtilBase;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.psi.util.*;
import com.intellij.util.JavaPsiConstructorUtil;
import com.intellij.util.ObjectUtils;
import org.jetbrains.annotations.Nls;
@@ -86,7 +83,7 @@ public final class JavaHighlightUtil {
@NotNull
public static String formatType(@Nullable PsiType type) {
return type == null ? PsiKeyword.NULL : type.getInternalCanonicalText();
return type == null ? PsiKeyword.NULL : PsiTypesUtil.removeExternalAnnotations(type).getInternalCanonicalText();
}
@Nullable

View File

@@ -41,7 +41,6 @@ import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.*;
import com.intellij.util.ArrayUtil;
import com.intellij.util.NullableFunction;
import com.intellij.util.ObjectUtils;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.JBIterable;
import com.intellij.util.containers.MultiMap;
@@ -280,16 +279,12 @@ public final class DfaPsiUtil {
*/
@NotNull
public static Nullability getFunctionalParameterNullability(PsiFunctionalExpression function, int index) {
Nullability nullability = inferLambdaParameterNullability(function, index);
if(nullability != Nullability.UNKNOWN) {
return nullability;
}
PsiClassType type = ObjectUtils.tryCast(LambdaUtil.getFunctionalInterfaceType(function, true), PsiClassType.class);
PsiClassType type = tryCast(LambdaUtil.getFunctionalInterfaceType(function, true), PsiClassType.class);
PsiMethod sam = LambdaUtil.getFunctionalInterfaceMethod(type);
if (sam != null) {
PsiParameter parameter = sam.getParameterList().getParameter(index);
if (parameter != null) {
nullability = getElementNullability(null, parameter);
Nullability nullability = getElementNullability(null, parameter);
if (nullability != Nullability.UNKNOWN) {
return nullability;
}
@@ -300,44 +295,6 @@ public final class DfaPsiUtil {
return Nullability.UNKNOWN;
}
@NotNull
private static Nullability inferLambdaParameterNullability(PsiFunctionalExpression lambda, int parameterIndex) {
PsiElement expression = lambda;
PsiElement expressionParent = lambda.getParent();
while(expressionParent instanceof PsiConditionalExpression || expressionParent instanceof PsiParenthesizedExpression) {
expression = expressionParent;
expressionParent = expressionParent.getParent();
}
if (expressionParent instanceof PsiExpressionList list && list.getParent() instanceof PsiMethodCallExpression call) {
PsiMethod method = call.resolveMethod();
if (method != null) {
int expressionIndex = ArrayUtil.find(list.getExpressions(), expression);
return getLambdaParameterNullability(method, expressionIndex, parameterIndex);
}
}
return Nullability.UNKNOWN;
}
private static final CallMatcher OPTIONAL_FUNCTIONS =
CallMatcher.instanceCall(JAVA_UTIL_OPTIONAL, "map", "filter", "ifPresent", "flatMap", "ifPresentOrElse");
private static final CallMatcher MAP_COMPUTE =
CallMatcher.instanceCall(JAVA_UTIL_MAP, "compute").parameterTypes("K", JAVA_UTIL_FUNCTION_BI_FUNCTION);
@NotNull
private static Nullability getLambdaParameterNullability(@NotNull PsiMethod method, int parameterIndex, int lambdaParameterIndex) {
if (OPTIONAL_FUNCTIONS.methodMatches(method)) {
if (parameterIndex == 0 && lambdaParameterIndex == 0) {
return Nullability.NOT_NULL;
}
}
else if (MAP_COMPUTE.methodMatches(method)) {
if (parameterIndex == 1 && lambdaParameterIndex == 1) {
return Nullability.NULLABLE;
}
}
return Nullability.UNKNOWN;
}
private static boolean isEnumPredefinedMethod(PsiMethod method) {
return CallMatcher.enumValueOf().methodMatches(method) || CallMatcher.enumValues().methodMatches(method);
}

View File

@@ -92,6 +92,15 @@ public abstract class ExternalAnnotationsManager {
public abstract @NotNull PsiAnnotation @NotNull [] findExternalAnnotations(@NotNull PsiModifierListOwner listOwner);
/**
* @param parent a type owner (field, method, or parameter)
* @param typePath a type path. See {@code ExternalTypeAnnotationContainer} for syntax
* @return external type annotations for a given type path
*/
public @NotNull PsiAnnotation @NotNull [] findExternalTypeAnnotations(@NotNull PsiModifierListOwner parent, @NotNull String typePath) {
return PsiAnnotation.EMPTY_ARRAY;
}
/**
* Returns external annotations associated with default
* constructor of the {@code aClass}, if the constructor exists.
@@ -115,7 +124,7 @@ public abstract class ExternalAnnotationsManager {
/**
* Returns external annotations with fully qualified name of {@code annotationFQN}
* associated with default constructor of the {@code aClass}, if the constructor exists.
*
* <p>
* Multiple annotations may be returned since there may be repeatable annotations
* or annotations from several external annotations roots.
*

View File

@@ -1262,6 +1262,7 @@ public final class LambdaUtil {
psiType = substitutor.substitute(parameters[i].getType());
if (!PsiTypesUtil.isDenotableType(psiType, lambdaExpression)) return null;
}
psiType = PsiTypesUtil.removeExternalAnnotations(psiType);
PsiAnnotation[] annotations = lambdaParameter.getAnnotations();
for (PsiAnnotation annotation : annotations) {

View File

@@ -1,6 +1,7 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.psi.util;
import com.intellij.codeInsight.ExternalAnnotationsManager;
import com.intellij.openapi.diagnostic.Attachment;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
@@ -713,6 +714,63 @@ public final class PsiTypesUtil {
return Collections.emptyList();
}
/**
* @param type input type
* @return type with removed external annotations (if any); on any level of depth
*/
public static @NotNull PsiType removeExternalAnnotations(@NotNull PsiType type) {
PsiAnnotation[] annotations = type.getAnnotations();
if (annotations.length > 0) {
List<PsiAnnotation> newAnnotations = ContainerUtil.filter(
annotations, annotation -> !ExternalAnnotationsManager.getInstance(annotation.getProject()).isExternalAnnotation(annotation));
if (newAnnotations.size() < annotations.length) {
type = type.annotate(TypeAnnotationProvider.Static.create(newAnnotations.toArray(PsiAnnotation.EMPTY_ARRAY)));
}
}
if (type instanceof PsiClassType) {
PsiClassType classType = (PsiClassType)type;
PsiClass psiClass = classType.resolve();
if (psiClass != null) {
PsiType[] parameters = classType.getParameters();
boolean changed = false;
for (int i = 0; i < parameters.length; i++) {
PsiType parameter = parameters[i];
PsiType updatedParameter = removeExternalAnnotations(parameter);
parameters[i] = updatedParameter;
changed |= updatedParameter != parameter;
}
if (changed) {
return JavaPsiFacade.getElementFactory(psiClass.getProject()).createType(psiClass, parameters);
}
}
return type;
}
else if (type instanceof PsiArrayType) {
PsiArrayType arrayType = (PsiArrayType)type;
PsiType origComponentType = arrayType.getComponentType();
PsiType componentType = removeExternalAnnotations(origComponentType);
return componentType == origComponentType ? type : componentType.createArrayType();
}
else if (type instanceof PsiWildcardType) {
PsiWildcardType wildcardType = (PsiWildcardType)type;
PsiType bound = wildcardType.getBound();
return bound == null ? wildcardType :
wildcardType.isExtends() ? PsiWildcardType.createExtends(wildcardType.getManager(), removeExternalAnnotations(bound)) :
wildcardType.isSuper() ? PsiWildcardType.createSuper(wildcardType.getManager(), removeExternalAnnotations(bound)) :
wildcardType;
}
else if (type instanceof PsiIntersectionType) {
PsiIntersectionType intersectionType = (PsiIntersectionType)type;
PsiType[] conjuncts = intersectionType.getConjuncts();
PsiType[] newConjuncts = new PsiType[conjuncts.length];
for (int i = 0; i < conjuncts.length; i++) {
newConjuncts[i] = removeExternalAnnotations(conjuncts[i]);
}
return PsiIntersectionType.createIntersection(newConjuncts);
}
return type;
}
public static class TypeParameterSearcher extends PsiTypeVisitor<Boolean> {
private final Set<PsiTypeParameter> myTypeParams = new HashSet<>();

View File

@@ -99,13 +99,22 @@ public abstract class BaseExternalAnnotationsManager extends ExternalAnnotations
if (result.isEmpty()) return Collections.emptyList();
SmartList<PsiAnnotation> annotations = new SmartList<>();
for (AnnotationData data : result) {
if (annotationFQNs.contains(data.annotationClassFqName)) {
if (data.hasNoTypePath() && annotationFQNs.contains(data.annotationClassFqName)) {
annotations.add(data.getAnnotation(this));
}
}
return annotations;
}
@Override
public @NotNull PsiAnnotation @NotNull [] findExternalTypeAnnotations(@NotNull PsiModifierListOwner listOwner,
@NotNull String typePath) {
List<AnnotationData> result = collectExternalAnnotations(listOwner);
if (result.isEmpty()) return PsiAnnotation.EMPTY_ARRAY;
return StreamEx.of(result).filter(data -> typePath.equals(data.typePath))
.map(data -> data.getAnnotation(this)).toArray(PsiAnnotation.EMPTY_ARRAY);
}
@Override
public @Nullable List<PsiAnnotation> findDefaultConstructorExternalAnnotations(@NotNull PsiClass aClass, @NotNull String annotationFQN) {
if (aClass.getConstructors().length > 0) {
@@ -118,7 +127,7 @@ public abstract class BaseExternalAnnotationsManager extends ExternalAnnotations
private @NotNull List<PsiAnnotation> filterAnnotations(@NotNull List<AnnotationData> result, @NotNull String annotationFQN) {
SmartList<PsiAnnotation> annotations = new SmartList<>();
for (AnnotationData data : result) {
if (data.annotationClassFqName.equals(annotationFQN)) {
if (data.hasNoTypePath() && data.annotationClassFqName.equals(annotationFQN)) {
PsiAnnotation annotation = data.getAnnotation(this);
annotations.add(annotation);
}
@@ -132,12 +141,15 @@ public abstract class BaseExternalAnnotationsManager extends ExternalAnnotations
return null;
}
List<AnnotationData> result = collectDefaultConstructorExternalAnnotations(aClass);
return ContainerUtil.map(result, data -> data.getAnnotation(this));
return StreamEx.of(result)
.filter(AnnotationData::hasNoTypePath)
.map(data -> data.getAnnotation(this))
.toList();
}
@Override
public boolean isExternalAnnotationWritable(@NotNull PsiModifierListOwner listOwner, final @NotNull String annotationFQN) {
// note that this method doesn't cache it's result
// note that this method doesn't cache its result
List<AnnotationData> map = doCollect(listOwner, true);
return findByFQN(map, annotationFQN) != null;
}
@@ -150,6 +162,7 @@ public abstract class BaseExternalAnnotationsManager extends ExternalAnnotations
public @NotNull PsiAnnotation @NotNull [] findExternalAnnotations(final @NotNull PsiModifierListOwner listOwner) {
final List<AnnotationData> result = collectExternalAnnotations(listOwner);
return result.isEmpty() ? PsiAnnotation.EMPTY_ARRAY : StreamEx.of(result)
.filter(data -> data.typePath == null)
.map(data -> data.getAnnotation(this))
.toArray(PsiAnnotation.EMPTY_ARRAY);
}
@@ -359,7 +372,7 @@ public abstract class BaseExternalAnnotationsManager extends ExternalAnnotations
if (!hasInvalidAttribute(invalidXml)) {
return invalidXml;
}
// We assume that XML has single- and double-quote characters only for attribute values, therefore we don't any complex parsing,
// We assume that XML has single- and double-quote characters only for attribute values, therefore, we don't do any complex parsing,
// just have binary inAttribute state
StringBuilder buf = new StringBuilder(invalidXml.length());
boolean inAttribute = false;
@@ -441,14 +454,20 @@ public abstract class BaseExternalAnnotationsManager extends ExternalAnnotations
}
public static final class AnnotationData {
private final String annotationClassFqName;
private final String annotationParameters;
private final @NotNull String annotationClassFqName;
private final @NotNull String annotationParameters;
private final @Nullable String typePath;
private volatile PsiAnnotation myAnnotation;
private AnnotationData(@NotNull String fqn, @NotNull String parameters) {
private AnnotationData(@NotNull String fqn, @NotNull String parameters, @Nullable String typePath) {
annotationClassFqName = fqn;
annotationParameters = parameters;
this.typePath = typePath;
}
public boolean hasNoTypePath() {
return typePath == null;
}
public @NotNull PsiAnnotation getAnnotation(@NotNull BaseExternalAnnotationsManager context) {
@@ -466,17 +485,28 @@ public abstract class BaseExternalAnnotationsManager extends ExternalAnnotations
if (o == null || getClass() != o.getClass()) return false;
AnnotationData data = (AnnotationData)o;
return annotationClassFqName.equals(data.annotationClassFqName) && annotationParameters.equals(data.annotationParameters);
return annotationClassFqName.equals(data.annotationClassFqName) &&
annotationParameters.equals(data.annotationParameters) &&
Objects.equals(typePath, data.typePath);
}
@Override
public int hashCode() {
int result = annotationClassFqName.hashCode();
result = 31 * result + annotationParameters.hashCode();
result = 31 * result + Objects.hashCode(typePath);
return result;
}
/**
* Returns annotation typePath, as specified in XML. See {@link com.intellij.psi.impl.cache.ExternalTypeAnnotationContainer}
* for syntax description;
*/
@Nullable
public String getTypePath() {
return typePath;
}
@Override
public String toString() {
return annotationClassFqName + "(" + annotationParameters + ")";
@@ -509,6 +539,7 @@ public abstract class BaseExternalAnnotationsManager extends ExternalAnnotations
private String myExternalName;
private String myAnnotationFqn;
private String myTypePath;
private StringBuilder myArguments;
private DataParsingSaxHandler(@NotNull VirtualFile file, @Nullable BaseExternalAnnotationsManager manager) {
@@ -520,9 +551,11 @@ public abstract class BaseExternalAnnotationsManager extends ExternalAnnotations
public void startElement(String uri, String localName, String qName, Attributes attributes) {
if ("item".equals(qName)) {
myExternalName = attributes.getValue("name");
myTypePath = null;
}
else if ("annotation".equals(qName)) {
myAnnotationFqn = attributes.getValue("name");
myTypePath = attributes.getValue("typePath");
myArguments = new StringBuilder();
}
else if ("val".equals(qName)) {
@@ -542,16 +575,25 @@ public abstract class BaseExternalAnnotationsManager extends ExternalAnnotations
public void endElement(String uri, String localName, String qName) {
if ("item".equals(qName)) {
myExternalName = null;
myTypePath = null;
}
else if ("annotation".equals(qName) && myExternalName != null && myAnnotationFqn != null) {
for (AnnotationData existingData : myData.get(myExternalName)) {
if (existingData.annotationClassFqName.equals(myAnnotationFqn) && myExternalAnnotationsManager != null) {
myExternalAnnotationsManager.duplicateError(myFile, myExternalName, "Duplicate annotation '" + myAnnotationFqn + "'");
if (existingData.annotationClassFqName.equals(myAnnotationFqn)
&& Objects.equals(myTypePath, existingData.typePath)
&& myExternalAnnotationsManager != null) {
myExternalAnnotationsManager.duplicateError(myFile, myExternalName, "Duplicate annotation '" +
myAnnotationFqn +
"'"
+
(myTypePath == null
? ""
: " for type path '" + myTypePath + "'"));
}
}
String argumentsString = myArguments.length() == 0 ? "" : myExternalAnnotationsManager == null ? myArguments.toString() : myExternalAnnotationsManager.intern(myArguments.toString());
AnnotationData data = new AnnotationData(myAnnotationFqn, argumentsString);
AnnotationData data = new AnnotationData(myAnnotationFqn, argumentsString, myTypePath);
myData.add(myExternalName, myExternalAnnotationsManager == null ? data : myExternalAnnotationsManager.internAnnotationData(data));
myAnnotationFqn = null;

View File

@@ -0,0 +1,456 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.psi.impl.cache;
import com.intellij.openapi.util.NotNullLazyValue;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiImplUtil;
import com.intellij.psi.impl.compiled.ClsAnnotationParameterListImpl;
import com.intellij.psi.impl.compiled.ClsElementImpl;
import com.intellij.psi.impl.compiled.ClsJavaCodeReferenceElementImpl;
import com.intellij.psi.impl.java.stubs.impl.PsiAnnotationStubImpl;
import com.intellij.psi.impl.source.PsiClassReferenceType;
import com.intellij.psi.impl.source.SourceTreeToPsiMap;
import com.intellij.psi.impl.source.tree.TreeElement;
import com.intellij.psi.stubs.StubElement;
import com.intellij.psi.stubs.StubInputStream;
import com.intellij.psi.stubs.StubOutputStream;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.ObjectUtils;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
/**
* An immutable container that holds all the type annotations for some type (including internal type components).
*/
public final class ExplicitTypeAnnotationContainer implements TypeAnnotationContainer {
private final List<TypeAnnotationEntry> myList;
private ExplicitTypeAnnotationContainer(List<TypeAnnotationEntry> entries) {
if (entries.isEmpty()) {
throw new IllegalArgumentException("Empty container: use TypeAnnotationContainer.EMPTY instead");
}
myList = entries;
}
/**
* @return type annotation container for array element
* (assuming that this type annotation container is used for the array type)
*/
@Override
public @NotNull TypeAnnotationContainer forArrayElement() {
List<TypeAnnotationEntry> list = ContainerUtil.mapNotNull(myList, entry -> entry.forPathElement(Collector.ARRAY_ELEMENT));
return list.isEmpty() ? EMPTY : new ExplicitTypeAnnotationContainer(list);
}
/**
* @return type annotation container for enclosing class
* (assuming that this type annotation container is used for the inner class)
*/
@Override
public @NotNull TypeAnnotationContainer forEnclosingClass() {
List<TypeAnnotationEntry> list = ContainerUtil.mapNotNull(myList, entry -> entry.forPathElement(Collector.ENCLOSING_CLASS));
return list.isEmpty() ? EMPTY : new ExplicitTypeAnnotationContainer(list);
}
/**
* @return type annotation container for wildcard bound
* (assuming that this type annotation container is used for the bounded wildcard type)
*/
@Override
public @NotNull TypeAnnotationContainer forBound() {
List<TypeAnnotationEntry> list = ContainerUtil.mapNotNull(myList, entry -> entry.forPathElement(Collector.WILDCARD_BOUND));
return list.isEmpty() ? EMPTY : new ExplicitTypeAnnotationContainer(list);
}
/**
* @param index type argument index
* @return type annotation container for given type argument
* (assuming that this type annotation container is used for class type with type arguments)
*/
@Override
public @NotNull TypeAnnotationContainer forTypeArgument(int index) {
List<TypeAnnotationEntry> list = ContainerUtil.mapNotNull(myList, e -> e.forTypeArgument(index));
return list.isEmpty() ? EMPTY : new ExplicitTypeAnnotationContainer(list);
}
/**
* @param parent parent element for annotations
* @return TypeAnnotationProvider that provides all the top-level annotations
*/
@Override
public @NotNull TypeAnnotationProvider getProvider(PsiElement parent) {
return new TypeAnnotationContainerProvider(parent, ObjectUtils.tryCast(parent, PsiAnnotationOwner.class));
}
/**
* Creates PsiAnnotationStub elements for top-level annotations in this container
*
* @param parent parent stub
*/
public void createAnnotationStubs(StubElement<?> parent) {
for (TypeAnnotationEntry entry : myList) {
if (entry.myPath.length == 0) {
new PsiAnnotationStubImpl(parent, entry.myText);
}
}
}
/**
* @param type type
* @param context context PsiElement
* @return type annotated with annotations from this container
*/
@Override
public @NotNull PsiType applyTo(@NotNull PsiType type, @NotNull PsiElement context) {
if (type instanceof PsiArrayType) {
PsiType componentType = ((PsiArrayType)type).getComponentType();
PsiType modifiedComponentType = forArrayElement().applyTo(componentType, context);
if (componentType != modifiedComponentType) {
type = type instanceof PsiEllipsisType ? new PsiEllipsisType(modifiedComponentType) : modifiedComponentType.createArrayType();
}
}
else if (type instanceof PsiClassReferenceType) {
PsiJavaCodeReferenceElement reference = ((PsiClassReferenceType)type).getReference();
PsiJavaCodeReferenceElement modifiedReference = annotateReference(reference, context);
if (modifiedReference != reference) {
type = new PsiClassReferenceType(modifiedReference, PsiUtil.getLanguageLevel(context), type.getAnnotationProvider());
}
if (modifiedReference.isQualified()) {
return type;
}
}
else if (type instanceof PsiWildcardType) {
PsiWildcardType wildcardType = (PsiWildcardType)type;
PsiType bound = wildcardType.getBound();
if (bound != null) {
PsiType modifiedBound = forBound().applyTo(bound, context);
if (modifiedBound != bound) {
if (wildcardType.isExtends()) {
type = PsiWildcardType.createExtends(context.getManager(), modifiedBound);
}
else if (wildcardType.isSuper()) {
type = PsiWildcardType.createSuper(context.getManager(), modifiedBound);
}
}
}
}
return type.annotate(getProvider(context));
}
@Override
public @NotNull PsiJavaCodeReferenceElement annotateReference(@NotNull PsiJavaCodeReferenceElement reference,
@NotNull PsiElement context) {
PsiReferenceParameterList list = reference.getParameterList();
PsiJavaCodeReferenceElement copy = reference;
PsiElement qualifier = reference.getQualifier();
if (qualifier != null) {
PsiJavaCodeReferenceElement modifiedQualifier =
forEnclosingClass().annotateReference((PsiJavaCodeReferenceElement)qualifier, context);
if (modifiedQualifier != qualifier) {
copy = (PsiJavaCodeReferenceElement)reference.copy();
Objects.requireNonNull(copy.getQualifier()).replace(modifiedQualifier);
}
StringBuilder refText = null;
for (TypeAnnotationEntry entry : myList) {
if (entry.myPath.length == 0) {
if (refText == null) {
refText = new StringBuilder(modifiedQualifier.getText());
refText.append(".");
}
refText.append(entry.myText).append(' ');
}
}
if (refText != null) {
boolean startCopy = false;
for (PsiElement child = reference.getFirstChild(); child != null; child = child.getNextSibling()) {
if (startCopy) {
refText.append(child.getText());
}
if (PsiUtil.isJavaToken(child, JavaTokenType.DOT)) {
startCopy = true;
}
}
copy = JavaPsiFacade.getElementFactory(context.getProject()).createReferenceFromText(refText.toString(), context);
}
}
if (list != null) {
PsiTypeElement[] elements = list.getTypeParameterElements();
for (int i = 0; i < elements.length; i++) {
PsiType parameter = elements[i].getType();
PsiType modifiedParameter = forTypeArgument(i).applyTo(parameter, context);
if (parameter != modifiedParameter) {
if (copy == reference) {
copy = (PsiJavaCodeReferenceElement)reference.copy();
}
Objects.requireNonNull(copy.getParameterList()).getTypeParameterElements()[i]
.replace(JavaPsiFacade.getElementFactory(context.getProject()).createTypeElement(modifiedParameter));
}
}
}
return copy;
}
/**
* Serializes TypeAnnotationContainer into the supplied stream.
*
* @param dataStream stream to write to
* @param container a container to serialize
* @throws IOException if the stream throws
*/
public static void writeTypeAnnotations(@NotNull StubOutputStream dataStream, @NotNull ExplicitTypeAnnotationContainer container)
throws IOException {
dataStream.writeShort(container.myList.size());
for (TypeAnnotationEntry entry : container.myList) {
dataStream.writeShort(entry.myPath.length);
dataStream.write(entry.myPath);
dataStream.writeUTFFast(entry.myText);
}
}
/**
* Reads TypeAnnotationContainer from the supplied stream.
*
* @param dataStream stream to read from
* @return deserialized TypeAnnotationContainer
* @throws IOException if the stream throws
*/
public static @NotNull TypeAnnotationContainer readTypeAnnotations(@NotNull StubInputStream dataStream) throws IOException {
short count = dataStream.readShort();
if (count == 0) return EMPTY;
TypeAnnotationEntry[] entries = new TypeAnnotationEntry[count];
for (int i = 0; i < count; i++) {
short pathLength = dataStream.readShort();
byte[] path = new byte[pathLength];
dataStream.readFully(path);
String text = dataStream.readUTFFast();
entries[i] = new TypeAnnotationEntry(path, text);
}
return new ExplicitTypeAnnotationContainer(Arrays.asList(entries));
}
@Override
public String toString() {
return StringUtil.join(myList, "\n");
}
private static @NotNull String encodePath(byte @NotNull [] path) {
StringBuilder result = new StringBuilder();
int pos = 0;
while (pos < path.length) {
switch (path[pos]) {
case Collector.ARRAY_ELEMENT:
result.append('[');
break;
case Collector.ENCLOSING_CLASS:
result.append('.');
break;
case Collector.WILDCARD_BOUND:
result.append('*');
break;
case Collector.TYPE_ARGUMENT:
result.append(path[++pos]).append(';');
break;
}
pos++;
}
return result.toString();
}
public static class Collector {
public static final byte ARRAY_ELEMENT = 0;
public static final byte ENCLOSING_CLASS = 1;
public static final byte WILDCARD_BOUND = 2;
public static final byte TYPE_ARGUMENT = 3;
private final @NotNull ArrayList<TypeAnnotationEntry> myList = new ArrayList<>();
protected final @NotNull TypeInfo myTypeInfo;
public Collector(@NotNull TypeInfo info) {
myTypeInfo = info;
}
public void add(byte @NotNull [] path, @NotNull String text) {
myList.add(new TypeAnnotationEntry(path, text));
}
public void install() {
if (myList.isEmpty()) {
myTypeInfo.setTypeAnnotations(EMPTY);
}
else {
myList.trimToSize();
myTypeInfo.setTypeAnnotations(new ExplicitTypeAnnotationContainer(myList));
}
}
}
private static class TypeAnnotationEntry {
/**
* path is stored as the sequence of ARRAY_ELEMENT, ENCLOSING_CLASS, WILDCARD_BOUND and TYPE_ARGUMENT bytes.
* The TYPE_ARGUMENT byte is followed by the type argument index byte.
*/
final byte @NotNull [] myPath;
final @NotNull String myText;
private TypeAnnotationEntry(byte @NotNull [] path, @NotNull String text) {
myPath = path.length == 0 ? ArrayUtil.EMPTY_BYTE_ARRAY : path;
myText = text;
}
private TypeAnnotationEntry forPathElement(int wanted) {
if (myPath.length > 0 && myPath[0] == wanted) {
return new TypeAnnotationEntry(Arrays.copyOfRange(myPath, 1, myPath.length), myText);
}
return null;
}
public TypeAnnotationEntry forTypeArgument(int index) {
if (myPath.length > 1 && myPath[0] == Collector.TYPE_ARGUMENT && myPath[1] == index) {
return new TypeAnnotationEntry(Arrays.copyOfRange(myPath, 2, myPath.length), myText);
}
return null;
}
@Override
public String toString() {
return encodePath(myPath) + "->" + myText;
}
}
static class ClsTypeAnnotationImpl extends ClsElementImpl implements PsiAnnotation {
private final NotNullLazyValue<ClsJavaCodeReferenceElementImpl> myReferenceElement;
private final NotNullLazyValue<ClsAnnotationParameterListImpl> myParameterList;
private final PsiElement myParent;
private final @Nullable PsiAnnotationOwner myOwner;
private final String myText;
ClsTypeAnnotationImpl(PsiElement parent, @Nullable PsiAnnotationOwner owner, String text) {
myParent = parent;
myOwner = owner;
myText = text;
myReferenceElement = NotNullLazyValue.atomicLazy(() -> {
int index = myText.indexOf('(');
String refText = index > 0 ? myText.substring(1, index) : myText.substring(1);
return new ClsJavaCodeReferenceElementImpl(this, refText);
});
myParameterList = NotNullLazyValue.atomicLazy(() -> {
PsiNameValuePair[] attrs = myText.indexOf('(') > 0
? JavaPsiFacade.getElementFactory(getProject()).createAnnotationFromText(myText, myParent)
.getParameterList().getAttributes()
: PsiNameValuePair.EMPTY_ARRAY;
return new ClsAnnotationParameterListImpl(this, attrs);
});
}
@Override
public @NotNull PsiAnnotationParameterList getParameterList() {
return myParameterList.getValue();
}
@Override
public @Nullable String getQualifiedName() {
return getNameReferenceElement().getCanonicalText();
}
@Override
public @NotNull PsiJavaCodeReferenceElement getNameReferenceElement() {
return myReferenceElement.getValue();
}
@Override
public @Nullable PsiAnnotationMemberValue findAttributeValue(@Nullable String attributeName) {
return PsiImplUtil.findAttributeValue(this, attributeName);
}
@Override
public @Nullable PsiAnnotationMemberValue findDeclaredAttributeValue(@Nullable String attributeName) {
return PsiImplUtil.findDeclaredAttributeValue(this, attributeName);
}
@Override
public <T extends PsiAnnotationMemberValue> T setDeclaredAttributeValue(@Nullable String attributeName, @Nullable T value) {
throw new UnsupportedOperationException();
}
@Override
public @Nullable PsiAnnotationOwner getOwner() {
return myOwner;
}
@Override
public void appendMirrorText(int indentLevel, @NotNull StringBuilder buffer) {
buffer.append(myText);
}
@Override
public String getText() {
return myText;
}
@Override
protected void setMirror(@NotNull TreeElement element) throws InvalidMirrorException {
setMirrorCheckingType(element, null);
PsiAnnotation mirror = SourceTreeToPsiMap.treeToPsiNotNull(element);
setMirror(getNameReferenceElement(), mirror.getNameReferenceElement());
setMirror(getParameterList(), mirror.getParameterList());
}
@Override
public PsiElement @NotNull [] getChildren() {
return new PsiElement[]{myReferenceElement.getValue(), getParameterList()};
}
@Override
public PsiElement getParent() {
return myParent;
}
@Override
public void accept(@NotNull PsiElementVisitor visitor) {
if (visitor instanceof JavaElementVisitor) {
((JavaElementVisitor)visitor).visitAnnotation(this);
}
else {
visitor.visitElement(this);
}
}
}
private class TypeAnnotationContainerProvider implements TypeAnnotationProvider {
private final PsiElement myParent;
private final @Nullable PsiAnnotationOwner myOwner;
private TypeAnnotationContainerProvider(PsiElement parent, @Nullable PsiAnnotationOwner owner) {
myParent = parent;
myOwner = owner;
}
@Override
public @NotNull TypeAnnotationProvider withOwner(@NotNull PsiAnnotationOwner owner) {
return new TypeAnnotationContainerProvider(myParent, owner);
}
@Override
public @NotNull PsiAnnotation @NotNull [] getAnnotations() {
List<PsiAnnotation> result = new ArrayList<>();
for (TypeAnnotationEntry entry : myList) {
if (entry.myPath.length == 0) {
PsiAnnotation anno = myParent instanceof PsiCompiledElement ? new ClsTypeAnnotationImpl(myParent, myOwner, entry.myText) :
JavaPsiFacade.getElementFactory(myParent.getProject()).createAnnotationFromText(entry.myText, myParent);
result.add(anno);
}
}
return result.toArray(PsiAnnotation.EMPTY_ARRAY);
}
}
}

View File

@@ -0,0 +1,75 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.psi.impl.cache;
import com.intellij.codeInsight.ExternalAnnotationsManager;
import com.intellij.psi.*;
import org.jetbrains.annotations.NotNull;
/**
* A container that reports external type annotations. External type annotations are described in annotation.xml files
* with additional {@code typePath} attribute. The attribute syntax is the following:
* <ul>
* <li>{@code digit;} - zero-based type argument
* <li>{@code *} (asterisk) - bound of a wildcard type
* <li>{@code [} (left bracket) - array element
* <li>{@code .} (dot) - enclosing type of inner type
* </ul>
* E.g., for type {@code Consumer<? super T>} the typePath {@code 0;*} points to {@code T}
*/
public final class ExternalTypeAnnotationContainer implements TypeAnnotationContainer {
@NotNull private final String myTypePath;
@NotNull private final PsiModifierListOwner myOwner;
private ExternalTypeAnnotationContainer(@NotNull String typePath, @NotNull PsiModifierListOwner owner) {
myTypePath = typePath;
myOwner = owner;
}
@Override
public @NotNull TypeAnnotationContainer forArrayElement() {
return new ExternalTypeAnnotationContainer(myTypePath + "[", myOwner);
}
@Override
public @NotNull TypeAnnotationContainer forEnclosingClass() {
return new ExternalTypeAnnotationContainer(myTypePath + ".", myOwner);
}
@Override
public @NotNull TypeAnnotationContainer forBound() {
return new ExternalTypeAnnotationContainer(myTypePath + "*", myOwner);
}
@Override
public @NotNull TypeAnnotationContainer forTypeArgument(int index) {
return new ExternalTypeAnnotationContainer(myTypePath + index + ";", myOwner);
}
@Override
public @NotNull TypeAnnotationProvider getProvider(PsiElement parent) {
// We don't expect any top-level type annotations: they will be stored as element (method/field/parameter) annotations,
// so let's spare some memory and avoid creating a provider
if (myTypePath.isEmpty()) return TypeAnnotationProvider.EMPTY;
return new TypeAnnotationProvider() {
@Override
public @NotNull PsiAnnotation @NotNull [] getAnnotations() {
return ExternalAnnotationsManager.getInstance(myOwner.getProject()).findExternalTypeAnnotations(myOwner, myTypePath);
}
};
}
@Override
public @NotNull PsiType applyTo(@NotNull PsiType type, @NotNull PsiElement context) {
throw new UnsupportedOperationException();
}
@Override
public @NotNull PsiJavaCodeReferenceElement annotateReference(@NotNull PsiJavaCodeReferenceElement reference,
@NotNull PsiElement context) {
throw new UnsupportedOperationException();
}
public static @NotNull TypeAnnotationContainer create(@NotNull PsiModifierListOwner owner) {
return new ExternalTypeAnnotationContainer("", owner);
}
}

View File

@@ -1,455 +1,97 @@
// Copyright 2000-2020 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.
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.psi.impl.cache;
import com.intellij.openapi.util.NotNullLazyValue;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiImplUtil;
import com.intellij.psi.impl.compiled.ClsAnnotationParameterListImpl;
import com.intellij.psi.impl.compiled.ClsElementImpl;
import com.intellij.psi.impl.compiled.ClsJavaCodeReferenceElementImpl;
import com.intellij.psi.impl.java.stubs.impl.PsiAnnotationStubImpl;
import com.intellij.psi.impl.source.PsiClassReferenceType;
import com.intellij.psi.impl.source.SourceTreeToPsiMap;
import com.intellij.psi.impl.source.tree.TreeElement;
import com.intellij.psi.stubs.StubElement;
import com.intellij.psi.stubs.StubInputStream;
import com.intellij.psi.stubs.StubOutputStream;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.ObjectUtils;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiJavaCodeReferenceElement;
import com.intellij.psi.PsiType;
import com.intellij.psi.TypeAnnotationProvider;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.util.*;
/**
* An immutable container that holds all the type annotations for some type (including internal type components).
* A container of all type annotations for given type, including structural children of the type
* (type arguments, wildcard bounds, outer types, array element types)
*/
public class TypeAnnotationContainer {
@ApiStatus.NonExtendable
public interface TypeAnnotationContainer {
/**
* A container that contains no type annotations.
*/
public static final TypeAnnotationContainer EMPTY = new TypeAnnotationContainer(Collections.emptyList());
TypeAnnotationContainer EMPTY = new TypeAnnotationContainer() {
@Override
public @NotNull TypeAnnotationContainer forArrayElement() {
return this;
}
private final List<TypeAnnotationEntry> myList;
@Override
public @NotNull TypeAnnotationContainer forEnclosingClass() {
return this;
}
private TypeAnnotationContainer(List<TypeAnnotationEntry> entries) {
myList = entries;
}
@Override
public @NotNull TypeAnnotationContainer forBound() {
return this;
}
@Override
public @NotNull TypeAnnotationContainer forTypeArgument(int index) {
return this;
}
@Override
public @NotNull TypeAnnotationProvider getProvider(PsiElement parent) {
return TypeAnnotationProvider.EMPTY;
}
@Override
public @NotNull PsiType applyTo(@NotNull PsiType type, @NotNull PsiElement context) {
return type;
}
@Override
public @NotNull PsiJavaCodeReferenceElement annotateReference(@NotNull PsiJavaCodeReferenceElement reference,
@NotNull PsiElement context) {
return reference;
}
};
/**
* @return type annotation container for array element
* (assuming that this type annotation container is used for the array type)
* @return a derived container that contains annotations for an array element,
* assuming that this container is used for the array
*/
public @NotNull TypeAnnotationContainer forArrayElement() {
if (isEmpty()) return this;
List<TypeAnnotationEntry> list = ContainerUtil.mapNotNull(myList, entry -> entry.forPathElement(Collector.ARRAY_ELEMENT));
return list.isEmpty() ? EMPTY : new TypeAnnotationContainer(list);
}
@NotNull TypeAnnotationContainer forArrayElement();
/**
* @return type annotation container for enclosing class
* (assuming that this type annotation container is used for the inner class)
* @return a derived container that contains annotations for enclosing class,
* assuming that this container is used for the inner class
*/
public @NotNull TypeAnnotationContainer forEnclosingClass() {
if (isEmpty()) return this;
List<TypeAnnotationEntry> list = ContainerUtil.mapNotNull(myList, entry -> entry.forPathElement(Collector.ENCLOSING_CLASS));
return list.isEmpty() ? EMPTY : new TypeAnnotationContainer(list);
}
@NotNull TypeAnnotationContainer forEnclosingClass();
/**
* @return type annotation container for wildcard bound
* (assuming that this type annotation container is used for the bounded wildcard type)
*/
public @NotNull TypeAnnotationContainer forBound() {
if (isEmpty()) return this;
List<TypeAnnotationEntry> list = ContainerUtil.mapNotNull(myList, entry -> entry.forPathElement(Collector.WILDCARD_BOUND));
return list.isEmpty() ? EMPTY : new TypeAnnotationContainer(list);
}
@NotNull TypeAnnotationContainer forBound();
/**
* @param index type argument index
* Returns a type annotation container for the given type argument index.
* This is used for types that have type arguments, and it provides the
* annotations associated with a specific type argument.
*
* @param index type argument index, zero-based
* @return type annotation container for given type argument
* (assuming that this type annotation container is used for class type with type arguments)
* (assuming that this type annotation container is used for a class type with type arguments)
*/
public @NotNull TypeAnnotationContainer forTypeArgument(int index) {
if (isEmpty()) return this;
List<TypeAnnotationEntry> list = ContainerUtil.mapNotNull(myList, e -> e.forTypeArgument(index));
return list.isEmpty() ? EMPTY : new TypeAnnotationContainer(list);
}
@NotNull TypeAnnotationContainer forTypeArgument(int index);
/**
* @return true if this type annotation container contains no type annotations
* @param parent parent PSI element for context
* @return TypeAnnotationProvider
*/
public boolean isEmpty() {
return myList.isEmpty();
}
@NotNull TypeAnnotationProvider getProvider(PsiElement parent);
/**
* @param parent parent element for annotations
* @return TypeAnnotationProvider that provides all the top-level annotations
*/
public TypeAnnotationProvider getProvider(PsiElement parent) {
if (isEmpty()) return TypeAnnotationProvider.EMPTY;
return new TypeAnnotationContainerProvider(parent, ObjectUtils.tryCast(parent, PsiAnnotationOwner.class));
}
@NotNull PsiType applyTo(@NotNull PsiType type, @NotNull PsiElement context);
/**
* Creates PsiAnnotationStub elements for top-level annotations in this container
*
* @param parent parent stub
*/
public void createAnnotationStubs(StubElement<?> parent) {
for (TypeAnnotationEntry entry : myList) {
if (entry.myPath.length == 0) {
new PsiAnnotationStubImpl(parent, entry.myText);
}
}
}
/**
* @param type type
* @param context context PsiElement
* @return type annotated with annotations from this container
*/
public @NotNull PsiType applyTo(@NotNull PsiType type, @NotNull PsiElement context) {
if (isEmpty()) return type;
if (type instanceof PsiArrayType) {
PsiType componentType = ((PsiArrayType)type).getComponentType();
PsiType modifiedComponentType = forArrayElement().applyTo(componentType, context);
if (componentType != modifiedComponentType) {
type = type instanceof PsiEllipsisType ? new PsiEllipsisType(modifiedComponentType) : modifiedComponentType.createArrayType();
}
}
else if (type instanceof PsiClassReferenceType) {
PsiJavaCodeReferenceElement reference = ((PsiClassReferenceType)type).getReference();
PsiJavaCodeReferenceElement modifiedReference = annotateReference(reference, context);
if (modifiedReference != reference) {
type = new PsiClassReferenceType(modifiedReference, PsiUtil.getLanguageLevel(context), type.getAnnotationProvider());
}
if (modifiedReference.isQualified()) {
return type;
}
}
else if (type instanceof PsiWildcardType) {
PsiWildcardType wildcardType = (PsiWildcardType)type;
PsiType bound = wildcardType.getBound();
if (bound != null) {
PsiType modifiedBound = forBound().applyTo(bound, context);
if (modifiedBound != bound) {
if (wildcardType.isExtends()) {
type = PsiWildcardType.createExtends(context.getManager(), modifiedBound);
}
else if (wildcardType.isSuper()) {
type = PsiWildcardType.createSuper(context.getManager(), modifiedBound);
}
}
}
}
return type.annotate(getProvider(context));
}
private @NotNull PsiJavaCodeReferenceElement annotateReference(@NotNull PsiJavaCodeReferenceElement reference,
@NotNull PsiElement context) {
PsiReferenceParameterList list = reference.getParameterList();
PsiJavaCodeReferenceElement copy = reference;
PsiElement qualifier = reference.getQualifier();
if (qualifier != null) {
PsiJavaCodeReferenceElement modifiedQualifier =
forEnclosingClass().annotateReference((PsiJavaCodeReferenceElement)qualifier, context);
if (modifiedQualifier != qualifier) {
copy = (PsiJavaCodeReferenceElement)reference.copy();
Objects.requireNonNull(copy.getQualifier()).replace(modifiedQualifier);
}
StringBuilder refText = null;
for (TypeAnnotationEntry entry : myList) {
if (entry.myPath.length == 0) {
if (refText == null) {
refText = new StringBuilder(modifiedQualifier.getText());
refText.append(".");
}
refText.append(entry.myText).append(' ');
}
}
if (refText != null) {
boolean startCopy = false;
for (PsiElement child = reference.getFirstChild(); child != null; child = child.getNextSibling()) {
if (startCopy) {
refText.append(child.getText());
}
if (PsiUtil.isJavaToken(child, JavaTokenType.DOT)) {
startCopy = true;
}
}
copy = JavaPsiFacade.getElementFactory(context.getProject()).createReferenceFromText(refText.toString(), context);
}
}
if (list != null) {
PsiTypeElement[] elements = list.getTypeParameterElements();
for (int i = 0; i < elements.length; i++) {
PsiType parameter = elements[i].getType();
PsiType modifiedParameter = forTypeArgument(i).applyTo(parameter, context);
if (parameter != modifiedParameter) {
if (copy == reference) {
copy = (PsiJavaCodeReferenceElement)reference.copy();
}
Objects.requireNonNull(copy.getParameterList()).getTypeParameterElements()[i]
.replace(JavaPsiFacade.getElementFactory(context.getProject()).createTypeElement(modifiedParameter));
}
}
}
return copy;
}
/**
* Serializes TypeAnnotationContainer into the supplied stream.
*
* @param dataStream stream to write to
* @param container a container to serialize
* @throws IOException if the stream throws
*/
public static void writeTypeAnnotations(@NotNull StubOutputStream dataStream, @NotNull TypeAnnotationContainer container)
throws IOException {
dataStream.writeShort(container.myList.size());
for (TypeAnnotationEntry entry : container.myList) {
dataStream.writeShort(entry.myPath.length);
dataStream.write(entry.myPath);
dataStream.writeUTFFast(entry.myText);
}
}
/**
* Reads TypeAnnotationContainer from the supplied stream.
*
* @param dataStream stream to read from
* @return deserialized TypeAnnotationContainer
* @throws IOException if the stream throws
*/
public static @NotNull TypeAnnotationContainer readTypeAnnotations(@NotNull StubInputStream dataStream) throws IOException {
short count = dataStream.readShort();
TypeAnnotationEntry[] entries = new TypeAnnotationEntry[count];
for (int i = 0; i < count; i++) {
short pathLength = dataStream.readShort();
byte[] path = new byte[pathLength];
dataStream.readFully(path);
String text = dataStream.readUTFFast();
entries[i] = new TypeAnnotationEntry(path, text);
}
return new TypeAnnotationContainer(Arrays.asList(entries));
}
@Override
public String toString() {
return StringUtil.join(myList, "\n");
}
public static class Collector {
public static final byte ARRAY_ELEMENT = 0;
public static final byte ENCLOSING_CLASS = 1;
public static final byte WILDCARD_BOUND = 2;
public static final byte TYPE_ARGUMENT = 3;
private final @NotNull ArrayList<TypeAnnotationEntry> myList = new ArrayList<>();
protected final @NotNull TypeInfo myTypeInfo;
public Collector(@NotNull TypeInfo info) {
myTypeInfo = info;
}
public void add(byte @NotNull [] path, @NotNull String text) {
myList.add(new TypeAnnotationEntry(path, text));
}
public void install() {
if (myList.isEmpty()) {
myTypeInfo.setTypeAnnotations(EMPTY);
}
else {
myList.trimToSize();
myTypeInfo.setTypeAnnotations(new TypeAnnotationContainer(myList));
}
}
}
private static class TypeAnnotationEntry {
/**
* path is stored as the sequence of ARRAY_ELEMENT, ENCLOSING_CLASS, WILDCARD_BOUND and TYPE_ARGUMENT bytes.
* The TYPE_ARGUMENT byte is followed by the type argument index byte.
*/
final byte @NotNull [] myPath;
final @NotNull String myText;
private TypeAnnotationEntry(byte @NotNull [] path, @NotNull String text) {
myPath = path.length == 0 ? ArrayUtil.EMPTY_BYTE_ARRAY : path;
myText = text;
}
private TypeAnnotationEntry forPathElement(int wanted) {
if (myPath.length > 0 && myPath[0] == wanted) {
return new TypeAnnotationEntry(Arrays.copyOfRange(myPath, 1, myPath.length), myText);
}
return null;
}
public TypeAnnotationEntry forTypeArgument(int index) {
if (myPath.length > 1 && myPath[0] == Collector.TYPE_ARGUMENT && myPath[1] == index) {
return new TypeAnnotationEntry(Arrays.copyOfRange(myPath, 2, myPath.length), myText);
}
return null;
}
@Override
public String toString() {
StringBuilder result = new StringBuilder();
int pos = 0;
while (pos < myPath.length) {
switch (myPath[pos]) {
case Collector.ARRAY_ELEMENT:
result.append('[');
break;
case Collector.ENCLOSING_CLASS:
result.append('.');
break;
case Collector.WILDCARD_BOUND:
result.append('*');
break;
case Collector.TYPE_ARGUMENT:
result.append(myPath[++pos]).append(';');
break;
}
pos++;
}
return result + "->" + myText;
}
}
static class ClsTypeAnnotationImpl extends ClsElementImpl implements PsiAnnotation {
private final NotNullLazyValue<ClsJavaCodeReferenceElementImpl> myReferenceElement;
private final NotNullLazyValue<ClsAnnotationParameterListImpl> myParameterList;
private final PsiElement myParent;
private final @Nullable PsiAnnotationOwner myOwner;
private final String myText;
ClsTypeAnnotationImpl(PsiElement parent, @Nullable PsiAnnotationOwner owner, String text) {
myParent = parent;
myOwner = owner;
myText = text;
myReferenceElement = NotNullLazyValue.atomicLazy(() -> {
int index = myText.indexOf('(');
String refText = index > 0 ? myText.substring(1, index) : myText.substring(1);
return new ClsJavaCodeReferenceElementImpl(this, refText);
});
myParameterList = NotNullLazyValue.atomicLazy(() -> {
PsiNameValuePair[] attrs = myText.indexOf('(') > 0
? JavaPsiFacade.getElementFactory(getProject()).createAnnotationFromText(myText, myParent)
.getParameterList().getAttributes()
: PsiNameValuePair.EMPTY_ARRAY;
return new ClsAnnotationParameterListImpl(this, attrs);
});
}
@Override
public @NotNull PsiAnnotationParameterList getParameterList() {
return myParameterList.getValue();
}
@Override
public @Nullable String getQualifiedName() {
return getNameReferenceElement().getCanonicalText();
}
@Override
public @NotNull PsiJavaCodeReferenceElement getNameReferenceElement() {
return myReferenceElement.getValue();
}
@Override
public @Nullable PsiAnnotationMemberValue findAttributeValue(@Nullable String attributeName) {
return PsiImplUtil.findAttributeValue(this, attributeName);
}
@Override
public @Nullable PsiAnnotationMemberValue findDeclaredAttributeValue(@Nullable String attributeName) {
return PsiImplUtil.findDeclaredAttributeValue(this, attributeName);
}
@Override
public <T extends PsiAnnotationMemberValue> T setDeclaredAttributeValue(@Nullable String attributeName, @Nullable T value) {
throw new UnsupportedOperationException();
}
@Override
public @Nullable PsiAnnotationOwner getOwner() {
return myOwner;
}
@Override
public void appendMirrorText(int indentLevel, @NotNull StringBuilder buffer) {
buffer.append(myText);
}
@Override
public String getText() {
return myText;
}
@Override
protected void setMirror(@NotNull TreeElement element) throws InvalidMirrorException {
setMirrorCheckingType(element, null);
PsiAnnotation mirror = SourceTreeToPsiMap.treeToPsiNotNull(element);
setMirror(getNameReferenceElement(), mirror.getNameReferenceElement());
setMirror(getParameterList(), mirror.getParameterList());
}
@Override
public PsiElement @NotNull [] getChildren() {
return new PsiElement[]{myReferenceElement.getValue(), getParameterList()};
}
@Override
public PsiElement getParent() {
return myParent;
}
@Override
public void accept(@NotNull PsiElementVisitor visitor) {
if (visitor instanceof JavaElementVisitor) {
((JavaElementVisitor)visitor).visitAnnotation(this);
}
else {
visitor.visitElement(this);
}
}
}
private class TypeAnnotationContainerProvider implements TypeAnnotationProvider {
private final PsiElement myParent;
private final @Nullable PsiAnnotationOwner myOwner;
private TypeAnnotationContainerProvider(PsiElement parent, @Nullable PsiAnnotationOwner owner) {
myParent = parent;
myOwner = owner;
}
@Override
public @NotNull TypeAnnotationProvider withOwner(@NotNull PsiAnnotationOwner owner) {
return new TypeAnnotationContainerProvider(myParent, owner);
}
@Override
public @NotNull PsiAnnotation @NotNull [] getAnnotations() {
List<PsiAnnotation> result = new ArrayList<>();
for (TypeAnnotationEntry entry : myList) {
if (entry.myPath.length == 0) {
PsiAnnotation anno = myParent instanceof PsiCompiledElement ? new ClsTypeAnnotationImpl(myParent, myOwner, entry.myText) :
JavaPsiFacade.getElementFactory(myParent.getProject()).createAnnotationFromText(entry.myText, myParent);
result.add(anno);
}
}
return result.toArray(PsiAnnotation.EMPTY_ARRAY);
}
}
@NotNull PsiJavaCodeReferenceElement annotateReference(@NotNull PsiJavaCodeReferenceElement reference,
@NotNull PsiElement context);
}

View File

@@ -417,15 +417,15 @@ public /*sealed*/ abstract class TypeInfo {
typeInfo = typeInfo.arrayOf();
}
byte[] prefix = new byte[arrayCount];
Arrays.fill(prefix, TypeAnnotationContainer.Collector.ARRAY_ELEMENT);
TypeAnnotationContainer.Collector collector = new TypeAnnotationContainer.Collector(typeInfo);
Arrays.fill(prefix, ExplicitTypeAnnotationContainer.Collector.ARRAY_ELEMENT);
ExplicitTypeAnnotationContainer.Collector collector = new ExplicitTypeAnnotationContainer.Collector(typeInfo);
collectAnnotations(typeInfo, collector, tree, typeElement, prefix);
collector.install();
return typeInfo;
}
private static void collectAnnotations(@NotNull TypeInfo info,
@NotNull TypeAnnotationContainer.Collector collector,
@NotNull ExplicitTypeAnnotationContainer.Collector collector,
@NotNull LighterAST tree,
@NotNull LighterASTNode element,
byte @NotNull [] prefix) {
@@ -451,10 +451,10 @@ public /*sealed*/ abstract class TypeInfo {
byte[] newPrefix;
if (bound) {
newPrefix = Arrays.copyOf(prefix, prefix.length + 1);
newPrefix[prefix.length] = TypeAnnotationContainer.Collector.WILDCARD_BOUND;
newPrefix[prefix.length] = ExplicitTypeAnnotationContainer.Collector.WILDCARD_BOUND;
} else {
newPrefix = Arrays.copyOf(prefix, prefix.length + arrayCount);
Arrays.fill(newPrefix, prefix.length, newPrefix.length, TypeAnnotationContainer.Collector.ARRAY_ELEMENT);
Arrays.fill(newPrefix, prefix.length, newPrefix.length, ExplicitTypeAnnotationContainer.Collector.ARRAY_ELEMENT);
}
collectAnnotations(((DerivedTypeInfo)info).child(), collector, tree, child, newPrefix);
}
@@ -464,14 +464,14 @@ public /*sealed*/ abstract class TypeInfo {
else if (tokenType == JavaElementType.ANNOTATION) {
String anno = LightTreeUtil.toFilteredString(tree, child, null);
byte[] typePath = Arrays.copyOf(prefix, prefix.length + nestingLevel);
Arrays.fill(typePath, prefix.length, typePath.length, TypeAnnotationContainer.Collector.ARRAY_ELEMENT);
Arrays.fill(typePath, prefix.length, typePath.length, ExplicitTypeAnnotationContainer.Collector.ARRAY_ELEMENT);
collector.add(typePath, anno);
}
}
}
private static void collectAnnotationsFromReference(@NotNull RefTypeInfo info,
TypeAnnotationContainer.@NotNull Collector collector,
ExplicitTypeAnnotationContainer.@NotNull Collector collector,
@NotNull LighterAST tree,
@NotNull LighterASTNode child,
byte @NotNull [] prefix) {
@@ -482,7 +482,7 @@ public /*sealed*/ abstract class TypeInfo {
RefTypeInfo outerType = info.outerType();
if (outerType != null) {
byte[] newPrefix = Arrays.copyOf(prefix, prefix.length + 1);
newPrefix[prefix.length] = TypeAnnotationContainer.Collector.ENCLOSING_CLASS;
newPrefix[prefix.length] = ExplicitTypeAnnotationContainer.Collector.ENCLOSING_CLASS;
collectAnnotationsFromReference(outerType, collector, tree, refChild, newPrefix);
}
}
@@ -493,7 +493,7 @@ public /*sealed*/ abstract class TypeInfo {
TypeInfo componentInfo = info.genericComponent(i);
if (componentInfo != null) {
byte[] newPrefix = Arrays.copyOf(prefix, prefix.length + 2);
newPrefix[prefix.length] = TypeAnnotationContainer.Collector.TYPE_ARGUMENT;
newPrefix[prefix.length] = ExplicitTypeAnnotationContainer.Collector.TYPE_ARGUMENT;
newPrefix[prefix.length + 1] = (byte)i;
collectAnnotations(componentInfo, collector, tree, subTypes.get(i), newPrefix);
}
@@ -700,12 +700,12 @@ public /*sealed*/ abstract class TypeInfo {
default:
info = kind.isReference() ? new RefTypeInfo(Objects.requireNonNull(kind.text)) : new SimpleTypeInfo(kind);
}
info.setTypeAnnotations(hasTypeAnnotations ? TypeAnnotationContainer.readTypeAnnotations(record) : TypeAnnotationContainer.EMPTY);
info.setTypeAnnotations(hasTypeAnnotations ? ExplicitTypeAnnotationContainer.readTypeAnnotations(record) : TypeAnnotationContainer.EMPTY);
return info;
}
public static void writeTYPE(@NotNull StubOutputStream dataStream, @NotNull TypeInfo typeInfo) throws IOException {
boolean hasTypeAnnotations = typeInfo.myTypeAnnotations != null && !typeInfo.myTypeAnnotations.isEmpty();
boolean hasTypeAnnotations = typeInfo.myTypeAnnotations instanceof ExplicitTypeAnnotationContainer;
dataStream.writeByte(typeInfo.kind.ordinal() | (hasTypeAnnotations ? HAS_TYPE_ANNOTATIONS : 0));
if (typeInfo instanceof DerivedTypeInfo) {
@@ -728,7 +728,7 @@ public /*sealed*/ abstract class TypeInfo {
}
}
if (hasTypeAnnotations) {
TypeAnnotationContainer.writeTypeAnnotations(dataStream, typeInfo.myTypeAnnotations);
ExplicitTypeAnnotationContainer.writeTypeAnnotations(dataStream, (ExplicitTypeAnnotationContainer)typeInfo.myTypeAnnotations);
}
}

View File

@@ -50,7 +50,7 @@ public class ClsJavaCodeReferenceElementImpl extends ClsElementImpl implements P
myAnnotations = annotations;
String prefix = PsiNameHelper.getOuterClassReference(canonicalText);
TypeAnnotationContainer container = prefix.isEmpty() ? TypeAnnotationContainer.EMPTY : annotations.forEnclosingClass();
myQualifier = container.isEmpty() ? null : new ClsJavaCodeReferenceElementImpl(this, prefix, container);
myQualifier = container == TypeAnnotationContainer.EMPTY ? null : new ClsJavaCodeReferenceElementImpl(this, prefix, container);
}
@Override

View File

@@ -1,7 +1,7 @@
// Copyright 2000-2020 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.psi.impl.compiled;
import com.intellij.psi.impl.cache.TypeAnnotationContainer;
import com.intellij.psi.impl.cache.ExplicitTypeAnnotationContainer;
import com.intellij.psi.impl.cache.TypeInfo;
import com.intellij.util.ArrayUtil;
import org.jetbrains.annotations.NotNull;
@@ -12,7 +12,7 @@ import org.jetbrains.org.objectweb.asm.TypePath;
import java.io.ByteArrayOutputStream;
import java.util.Arrays;
class ClsTypeAnnotationCollector extends TypeAnnotationContainer.Collector {
class ClsTypeAnnotationCollector extends ExplicitTypeAnnotationContainer.Collector {
private final @NotNull FirstPassData myFirstPassData;
ClsTypeAnnotationCollector(@NotNull TypeInfo info, @NotNull FirstPassData classInfo) {

View File

@@ -6,6 +6,7 @@ import com.intellij.openapi.util.NullableLazyValue;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiImplUtil;
import com.intellij.psi.impl.PsiJavaParserFacadeImpl;
import com.intellij.psi.impl.cache.ExternalTypeAnnotationContainer;
import com.intellij.psi.impl.cache.TypeAnnotationContainer;
import com.intellij.psi.impl.cache.TypeInfo;
import com.intellij.psi.impl.source.PsiClassReferenceType;
@@ -52,7 +53,8 @@ public class ClsTypeElementImpl extends ClsElementImpl implements PsiTypeElement
myParent = parent;
myTypeText = TypeInfo.internFrequentType(typeText);
myVariance = variance;
myAnnotations = annotations;
myAnnotations = annotations == TypeAnnotationContainer.EMPTY && parent instanceof PsiModifierListOwner ?
ExternalTypeAnnotationContainer.create((PsiModifierListOwner)parent) : annotations;
myChild = atomicLazyNullable(() -> calculateChild());
myCachedType = atomicLazy(() -> calculateType());
}

View File

@@ -1,6 +1,8 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.psi.impl.compiled;
import com.intellij.psi.impl.cache.ExplicitTypeAnnotationContainer;
import com.intellij.psi.impl.cache.TypeAnnotationContainer;
import com.intellij.psi.impl.cache.TypeInfo;
import com.intellij.psi.impl.cache.TypeInfo.TypeKind;
import com.intellij.psi.impl.java.stubs.JavaStubElementTypes;
@@ -26,7 +28,7 @@ public final class SignatureParsing {
private SignatureParsing() { }
/**
* A function to map JVM class names to {@link com.intellij.psi.impl.cache.TypeInfo.RefTypeInfo}.
* A function to map JVM class names to {@link TypeInfo.RefTypeInfo}.
* Normally, this function should take into account probable inner classes. This is done by {@link FirstPassData} implementation.
* If inner classes information is unavailable, use {@link StubBuildingVisitor#GUESSING_PROVIDER} for heuristic-based mapping
*/
@@ -138,7 +140,10 @@ public final class SignatureParsing {
private void createTypeParameter(PsiTypeParameterListStub listStub) {
PsiTypeParameterStub stub = new PsiTypeParameterStubImpl(listStub, this.myTypeParameter.text());
myTypeParameter.getTypeAnnotations().createAnnotationStubs(stub);
TypeAnnotationContainer annotations = myTypeParameter.getTypeAnnotations();
if (annotations instanceof ExplicitTypeAnnotationContainer) {
((ExplicitTypeAnnotationContainer)annotations).createAnnotationStubs(stub);
}
TypeInfo[] info = this.myBounds;
if (info.length > 0 && info[0] == null) {
info = Arrays.copyOfRange(info, 1, info.length);

View File

@@ -0,0 +1,17 @@
import java.util.concurrent.CompletableFuture;
import typeUse.*;
public class CompletableFutureWhenComplete {
native CompletableFuture<@NotNull String> supply();
void test() {
supply().whenComplete((s, t) -> {
if (t != null) {
System.out.println(t);
}
if (s != null) {
System.out.println(s);
}
});
}
}

View File

@@ -110,10 +110,46 @@ public class ExternalAnnotationsManagerTest extends LightPlatformTestCase {
PsiAnnotation annotation = annotationData.getAnnotation(manager);
String nameText = annotation.getNameReferenceElement().getText();
assertClassFqn(nameText, psiFile, externalName, null);
String typePath = annotationData.getTypePath();
if (typePath != null) {
String error = validatePath(typePath);
if (error != null) {
fail("Invalid typePath: " + error, psiFile, externalName);
}
}
}
}
}
private static String validatePath(String pathString) {
if (pathString.isEmpty()) {
return "Empty path";
}
for (int i = 0; i < pathString.length(); i++) {
char c = pathString.charAt(i);
switch (c) {
case '[', '*', '.':
break;
default:
if (c >= '0' && c <= '9') {
int j = i;
while (j < pathString.length() && pathString.charAt(j) >= '0' && pathString.charAt(j) <= '9') {
j++;
}
int result = Integer.parseInt(pathString.substring(i, j));
if (result >= 0 && result <= 255 && j < pathString.length() && pathString.charAt(j) == ';') {
//noinspection AssignmentToForLoopParameter
i = j; // Skip the ';' character
break;
}
}
return "Invalid path: " + pathString;
}
}
return null;
}
private PsiClass assertClassFqn(@NotNull String text,
@NotNull PsiFile psiFile,
@NotNull String externalName,

View File

@@ -401,4 +401,8 @@ public class DataFlowInspection8Test extends DataFlowInspectionTestCase {
public void testConsumedStreamWithoutInline() { doTest(); }
public void testLocalityAndConditionalExpression() { doTest(); }
public void testParallelStreamThreadId() { doTest(); }
public void testCompletableFutureWhenComplete() {
setupTypeUseAnnotations("typeUse", myFixture);
doTest();
}
}

View File

@@ -3703,6 +3703,7 @@
</item>
<item name='java.util.Optional java.util.Optional&lt;T&gt; filter(java.util.function.Predicate&lt;? super T&gt;) 0'>
<annotation name='org.jetbrains.annotations.NotNull'/>
<annotation name='org.jetbrains.annotations.NotNull' typePath="0;*"/>
</item>
<item name='java.util.Optional java.util.Optional&lt;T&gt; of(T) 0'>
<annotation name='org.intellij.lang.annotations.Flow'>
@@ -3717,15 +3718,19 @@
<item
name='java.util.Optional java.util.Optional&lt;U&gt; flatMap(java.util.function.Function&lt;? super T,? extends java.util.Optional&lt;? extends U&gt;&gt;) 0'>
<annotation name='org.jetbrains.annotations.NotNull'/>
<annotation name='org.jetbrains.annotations.NotNull' typePath="0;*"/>
</item>
<item name='java.util.Optional java.util.Optional&lt;U&gt; map(java.util.function.Function&lt;? super T,? extends U&gt;) 0'>
<annotation name='org.jetbrains.annotations.NotNull'/>
<annotation name='org.jetbrains.annotations.NotNull' typePath="0;*"/>
</item>
<item name='java.util.Optional void ifPresent(java.util.function.Consumer&lt;? super T&gt;) 0'>
<annotation name='org.jetbrains.annotations.NotNull'/>
<annotation name='org.jetbrains.annotations.NotNull' typePath="0;*"/>
</item>
<item name='java.util.Optional void ifPresentOrElse(java.util.function.Consumer&lt;? super T&gt;, java.lang.Runnable) 0'>
<annotation name='org.jetbrains.annotations.NotNull'/>
<annotation name='org.jetbrains.annotations.NotNull' typePath="0;*"/>
</item>
<item name='java.util.Properties java.lang.String getProperty(java.lang.String) 0'>
<annotation name='org.jetbrains.annotations.NonNls'/>

View File

@@ -241,6 +241,8 @@
<item
name='java.util.concurrent.CompletableFuture java.util.concurrent.CompletableFuture&lt;T&gt; whenComplete(java.util.function.BiConsumer&lt;? super T,? super java.lang.Throwable&gt;) 0'>
<annotation name='org.jetbrains.annotations.NotNull'/>
<annotation name='org.jetbrains.annotations.UnknownNullability' typePath="0;*"/>
<annotation name='org.jetbrains.annotations.UnknownNullability' typePath="1;*"/>
</item>
<item
name='java.util.concurrent.CompletableFuture java.util.concurrent.CompletableFuture&lt;T&gt; whenCompleteAsync(java.util.function.BiConsumer&lt;? super T,? super java.lang.Throwable&gt;)'>
@@ -249,6 +251,8 @@
<item
name='java.util.concurrent.CompletableFuture java.util.concurrent.CompletableFuture&lt;T&gt; whenCompleteAsync(java.util.function.BiConsumer&lt;? super T,? super java.lang.Throwable&gt;) 0'>
<annotation name='org.jetbrains.annotations.NotNull'/>
<annotation name='org.jetbrains.annotations.UnknownNullability' typePath="0;*"/>
<annotation name='org.jetbrains.annotations.UnknownNullability' typePath="1;*"/>
</item>
<item
name='java.util.concurrent.CompletableFuture java.util.concurrent.CompletableFuture&lt;T&gt; whenCompleteAsync(java.util.function.BiConsumer&lt;? super T,? super java.lang.Throwable&gt;, java.util.concurrent.Executor)'>
@@ -257,6 +261,8 @@
<item
name='java.util.concurrent.CompletableFuture java.util.concurrent.CompletableFuture&lt;T&gt; whenCompleteAsync(java.util.function.BiConsumer&lt;? super T,? super java.lang.Throwable&gt;, java.util.concurrent.Executor) 0'>
<annotation name='org.jetbrains.annotations.NotNull'/>
<annotation name='org.jetbrains.annotations.UnknownNullability' typePath="0;*"/>
<annotation name='org.jetbrains.annotations.UnknownNullability' typePath="1;*"/>
</item>
<item
name='java.util.concurrent.CompletableFuture java.util.concurrent.CompletableFuture&lt;U&gt; applyToEither(java.util.concurrent.CompletionStage&lt;? extends T&gt;, java.util.function.Function&lt;? super T,U&gt;)'>
@@ -707,6 +713,8 @@
<item
name='java.util.concurrent.CompletionStage java.util.concurrent.CompletionStage&lt;T&gt; whenComplete(java.util.function.BiConsumer&lt;? super T,? super java.lang.Throwable&gt;) 0'>
<annotation name='org.jetbrains.annotations.NotNull'/>
<annotation name='org.jetbrains.annotations.UnknownNullability' typePath="0;*"/>
<annotation name='org.jetbrains.annotations.UnknownNullability' typePath="1;*"/>
</item>
<item
name='java.util.concurrent.CompletionStage java.util.concurrent.CompletionStage&lt;T&gt; whenCompleteAsync(java.util.function.BiConsumer&lt;? super T,? super java.lang.Throwable&gt;)'>
@@ -715,6 +723,8 @@
<item
name='java.util.concurrent.CompletionStage java.util.concurrent.CompletionStage&lt;T&gt; whenCompleteAsync(java.util.function.BiConsumer&lt;? super T,? super java.lang.Throwable&gt;) 0'>
<annotation name='org.jetbrains.annotations.NotNull'/>
<annotation name='org.jetbrains.annotations.UnknownNullability' typePath="0;*"/>
<annotation name='org.jetbrains.annotations.UnknownNullability' typePath="1;*"/>
</item>
<item
name='java.util.concurrent.CompletionStage java.util.concurrent.CompletionStage&lt;T&gt; whenCompleteAsync(java.util.function.BiConsumer&lt;? super T,? super java.lang.Throwable&gt;, java.util.concurrent.Executor)'>
@@ -723,6 +733,8 @@
<item
name='java.util.concurrent.CompletionStage java.util.concurrent.CompletionStage&lt;T&gt; whenCompleteAsync(java.util.function.BiConsumer&lt;? super T,? super java.lang.Throwable&gt;, java.util.concurrent.Executor) 0'>
<annotation name='org.jetbrains.annotations.NotNull'/>
<annotation name='org.jetbrains.annotations.UnknownNullability' typePath="0;*"/>
<annotation name='org.jetbrains.annotations.UnknownNullability' typePath="1;*"/>
</item>
<item
name='java.util.concurrent.CompletionStage java.util.concurrent.CompletionStage&lt;U&gt; applyToEither(java.util.concurrent.CompletionStage&lt;? extends T&gt;, java.util.function.Function&lt;? super T,U&gt;)'>

View File

@@ -7,7 +7,7 @@ import java.util.stream.Stream
fun test(list: /*T1@*/List</*T0@*/String>) {
val x: /*T9@*/List</*T8@*/String> = list/*T1@List<T0@String>*/.stream()/*Stream<T0@String>!!L*/
.map</*T3@*/String>({ x: /*T2@*/String -> x/*T2@String*/ + ""/*LIT*//*LIT*/ }/*Function1<T2@String, T10@String>!!L*/)/*Stream<T3@String>*/
.collect</*T6@*/List</*T5@*/String>, /*T7@*/Any>(Collectors/*LIT*/.toList</*T4@*/String>()/*Collector<T4@String, *, MutableList<T4@String>>*/)/*T6@List<T5@String>*/
.collect</*T6@*/List</*T5@*/String>, /*T7@*/Any>(Collectors/*LIT*/.toList</*T4@*/String>()/*Collector<T4@String, *, MutableList<T4@String>>!!L*/)/*T6@List<T5@String>*/
}