/*
* Copyright 2010-2017 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 org.jetbrains.kotlin.resolve.lazy.descriptors;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiNameIdentifierOwner;
import kotlin.collections.CollectionsKt;
import kotlin.jvm.functions.Function1;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.ReadOnly;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.annotations.Annotations;
import org.jetbrains.kotlin.descriptors.impl.ClassDescriptorBase;
import org.jetbrains.kotlin.descriptors.impl.FunctionDescriptorImpl;
import org.jetbrains.kotlin.incremental.components.NoLookupLocation;
import org.jetbrains.kotlin.lexer.KtTokens;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.psi.psiUtil.KtPsiUtilKt;
import org.jetbrains.kotlin.psi.synthetics.SyntheticClassOrObjectDescriptor;
import org.jetbrains.kotlin.resolve.*;
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
import org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil;
import org.jetbrains.kotlin.resolve.lazy.LazyClassContext;
import org.jetbrains.kotlin.resolve.lazy.LazyEntity;
import org.jetbrains.kotlin.resolve.lazy.data.KtClassInfoUtil;
import org.jetbrains.kotlin.resolve.lazy.data.KtClassLikeInfo;
import org.jetbrains.kotlin.resolve.lazy.data.KtClassOrObjectInfo;
import org.jetbrains.kotlin.resolve.lazy.data.KtObjectInfo;
import org.jetbrains.kotlin.resolve.lazy.declarations.ClassMemberDeclarationProvider;
import org.jetbrains.kotlin.resolve.scopes.LexicalScope;
import org.jetbrains.kotlin.resolve.scopes.MemberScope;
import org.jetbrains.kotlin.resolve.scopes.StaticScopeForKotlinEnum;
import org.jetbrains.kotlin.resolve.source.KotlinSourceElementKt;
import org.jetbrains.kotlin.storage.MemoizedFunctionToNotNull;
import org.jetbrains.kotlin.storage.NotNullLazyValue;
import org.jetbrains.kotlin.storage.NullableLazyValue;
import org.jetbrains.kotlin.storage.StorageManager;
import org.jetbrains.kotlin.types.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import static kotlin.collections.CollectionsKt.firstOrNull;
import static org.jetbrains.kotlin.descriptors.Visibilities.PUBLIC;
import static org.jetbrains.kotlin.diagnostics.Errors.*;
import static org.jetbrains.kotlin.resolve.BindingContext.TYPE;
import static org.jetbrains.kotlin.resolve.ModifiersChecker.*;
public class LazyClassDescriptor extends ClassDescriptorBase implements ClassDescriptorWithResolutionScopes, LazyEntity {
private static final Function1<KotlinType, Boolean> VALID_SUPERTYPE = type -> {
assert !KotlinTypeKt.isError(type) : "Error types must be filtered out in DescriptorResolver";
return TypeUtils.getClassDescriptor(type) != null;
};
private final LazyClassContext c;
@Nullable // can be null in KtScript
private final KtClassOrObject classOrObject;
private final ClassMemberDeclarationProvider declarationProvider;
private final LazyClassTypeConstructor typeConstructor;
private final NotNullLazyValue<Modality> modality;
private final Visibility visibility;
private final ClassKind kind;
private final boolean isInner;
private final boolean isData;
private final boolean isHeader;
private final boolean isImpl;
private final Annotations annotations;
private final Annotations danglingAnnotations;
private final NullableLazyValue<ClassDescriptorWithResolutionScopes> companionObjectDescriptor;
private final MemoizedFunctionToNotNull<KtObjectDeclaration, ClassDescriptor> extraCompanionObjectDescriptors;
private final LazyClassMemberScope unsubstitutedMemberScope;
private final MemberScope staticScope;
private final NullableLazyValue<Void> forceResolveAllContents;
private final boolean isCompanionObject;
private final ClassResolutionScopesSupport resolutionScopesSupport;
private final NotNullLazyValue<List<TypeParameterDescriptor>> parameters;
private final NotNullLazyValue<LexicalScope> scopeForInitializerResolution;
private final NotNullLazyValue<Collection<ClassDescriptor>> sealedSubclasses;
public LazyClassDescriptor(
@NotNull LazyClassContext c,
@NotNull DeclarationDescriptor containingDeclaration,
@NotNull Name name,
@NotNull KtClassLikeInfo classLikeInfo,
boolean isExternal
) {
super(c.getStorageManager(), containingDeclaration, name,
KotlinSourceElementKt.toSourceElement(classLikeInfo.getCorrespondingClassOrObject()),
isExternal
);
this.c = c;
classOrObject = classLikeInfo.getCorrespondingClassOrObject();
if (classOrObject != null) {
this.c.getTrace().record(BindingContext.CLASS, classOrObject, this);
}
this.c.getTrace().record(BindingContext.FQNAME_TO_CLASS_DESCRIPTOR, DescriptorUtils.getFqName(this), this);
this.declarationProvider = c.getDeclarationProviderFactory().getClassMemberDeclarationProvider(classLikeInfo);
StorageManager storageManager = c.getStorageManager();
this.unsubstitutedMemberScope = createMemberScope(c, this.declarationProvider);
this.kind = classLikeInfo.getClassKind();
this.staticScope = kind == ClassKind.ENUM_CLASS ? new StaticScopeForKotlinEnum(storageManager, this) : MemberScope.Empty.INSTANCE;
this.typeConstructor = new LazyClassTypeConstructor();
this.isCompanionObject = classLikeInfo instanceof KtObjectInfo && ((KtObjectInfo) classLikeInfo).isCompanionObject();
KtModifierList modifierList = classLikeInfo.getModifierList();
if (kind.isSingleton()) {
this.modality = storageManager.createLazyValue(() -> Modality.FINAL);
}
else {
Modality defaultModality = kind == ClassKind.INTERFACE ? Modality.ABSTRACT : Modality.FINAL;
this.modality = storageManager.createLazyValue(
() -> resolveModalityFromModifiers(classOrObject, defaultModality, c.getTrace().getBindingContext(),
null, /* allowSealed = */ true));
}
boolean isLocal = classOrObject != null && KtPsiUtil.isLocal(classOrObject);
Visibility defaultVisibility;
if (kind == ClassKind.ENUM_ENTRY || (kind == ClassKind.OBJECT && isCompanionObject)) {
defaultVisibility = Visibilities.PUBLIC;
}
else {
defaultVisibility = Visibilities.DEFAULT_VISIBILITY;
}
this.visibility = isLocal ? Visibilities.LOCAL : resolveVisibilityFromModifiers(modifierList, defaultVisibility);
this.isInner = isInnerClass(modifierList) && !ModifiersChecker.isIllegalInner(this);
this.isData = modifierList != null && modifierList.hasModifier(KtTokens.DATA_KEYWORD);
this.isHeader = modifierList != null && modifierList.hasModifier(KtTokens.HEADER_KEYWORD);
this.isImpl = modifierList != null && modifierList.hasModifier(KtTokens.IMPL_KEYWORD);
// Annotation entries are taken from both own annotations (if any) and object literal annotations (if any)
List<KtAnnotationEntry> annotationEntries = new ArrayList<>();
if (classOrObject != null && classOrObject.getParent() instanceof KtObjectLiteralExpression) {
// TODO: it would be better to have separate ObjectLiteralDescriptor without so much magic
annotationEntries.addAll(KtPsiUtilKt.getAnnotationEntries((KtObjectLiteralExpression) classOrObject.getParent()));
}
if (modifierList != null) {
annotationEntries.addAll(modifierList.getAnnotationEntries());
}
if (!annotationEntries.isEmpty()) {
this.annotations = new LazyAnnotations(
new LazyAnnotationsContext(
c.getAnnotationResolver(),
storageManager,
c.getTrace()
) {
@NotNull
@Override
public LexicalScope getScope() {
return getOuterScope();
}
},
annotationEntries
);
}
else {
this.annotations = Annotations.Companion.getEMPTY();
}
List<KtAnnotationEntry> jetDanglingAnnotations = classLikeInfo.getDanglingAnnotations();
if (jetDanglingAnnotations.isEmpty()) {
this.danglingAnnotations = Annotations.Companion.getEMPTY();
}
else {
this.danglingAnnotations = new LazyAnnotations(
new LazyAnnotationsContext(
c.getAnnotationResolver(),
storageManager,
c.getTrace()
) {
@NotNull
@Override
public LexicalScope getScope() {
return getScopeForMemberDeclarationResolution();
}
},
jetDanglingAnnotations
);
}
this.companionObjectDescriptor = storageManager.createNullableLazyValue(
() -> computeCompanionObjectDescriptor(getCompanionObjectIfAllowed())
);
this.extraCompanionObjectDescriptors = storageManager.createMemoizedFunction(this::computeCompanionObjectDescriptor);
this.forceResolveAllContents = storageManager.createRecursionTolerantNullableLazyValue(() -> {
doForceResolveAllContents();
return null;
}, null);
this.resolutionScopesSupport = new ClassResolutionScopesSupport(this, storageManager, this::getOuterScope);
this.parameters = c.getStorageManager().createLazyValue(() -> {
KtClassLikeInfo classInfo = declarationProvider.getOwnerInfo();
KtTypeParameterList typeParameterList = classInfo.getTypeParameterList();
if (typeParameterList == null) return Collections.emptyList();
if (classInfo.getClassKind() == ClassKind.ENUM_CLASS) {
c.getTrace().report(TYPE_PARAMETERS_IN_ENUM.on(typeParameterList));
}
if (classInfo.getClassKind() == ClassKind.OBJECT) {
c.getTrace().report(TYPE_PARAMETERS_IN_OBJECT.on(typeParameterList));
}
List<KtTypeParameter> typeParameters = typeParameterList.getParameters();
if (typeParameters.isEmpty()) return Collections.emptyList();
List<TypeParameterDescriptor> parameters = new ArrayList<>(typeParameters.size());
for (int i = 0; i < typeParameters.size(); i++) {
parameters.add(new LazyTypeParameterDescriptor(c, this, typeParameters.get(i), i));
}
return parameters;
});
this.scopeForInitializerResolution = storageManager.createLazyValue(
() -> ClassResolutionScopesSupportKt.scopeForInitializerResolution(
this, createInitializerScopeParent(), classLikeInfo.getPrimaryConstructorParameters()
)
);
// TODO: only consider classes from the same file, not the whole package fragment
this.sealedSubclasses = storageManager.createLazyValue(() -> DescriptorUtilsKt.computeSealedSubclasses(this));
}
@NotNull
private DeclarationDescriptor createInitializerScopeParent() {
ConstructorDescriptor primaryConstructor = getUnsubstitutedPrimaryConstructor();
if (primaryConstructor != null) return primaryConstructor;
return new FunctionDescriptorImpl(
LazyClassDescriptor.this, null, Annotations.Companion.getEMPTY(), Name.special("<init-blocks>"),
CallableMemberDescriptor.Kind.SYNTHESIZED, SourceElement.NO_SOURCE
) {
{
initialize(null, null, Collections.emptyList(), Collections.emptyList(),
null, Modality.FINAL, Visibilities.PRIVATE);
}
@NotNull
@Override
protected FunctionDescriptorImpl createSubstitutedCopy(
@NotNull DeclarationDescriptor newOwner,
@Nullable FunctionDescriptor original,
@NotNull Kind kind,
@Nullable Name newName,
@NotNull Annotations annotations,
@NotNull SourceElement source
) {
throw new UnsupportedOperationException();
}
};
}
// NOTE: Called from constructor!
@NotNull
protected LazyClassMemberScope createMemberScope(
@NotNull LazyClassContext c,
@NotNull ClassMemberDeclarationProvider declarationProvider
) {
return new LazyClassMemberScope(c, declarationProvider, this, c.getTrace());
}
@NotNull
@Override
public MemberScope getUnsubstitutedMemberScope() {
return unsubstitutedMemberScope;
}
@NotNull
protected LexicalScope getOuterScope() {
return c.getDeclarationScopeProvider().getResolutionScopeForDeclaration(declarationProvider.getOwnerInfo().getScopeAnchor());
}
@Override
@NotNull
public LexicalScope getScopeForClassHeaderResolution() {
return resolutionScopesSupport.getScopeForClassHeaderResolution().invoke();
}
@Override
@NotNull
public LexicalScope getScopeForConstructorHeaderResolution() {
return resolutionScopesSupport.getScopeForConstructorHeaderResolution().invoke();
}
@Override
@NotNull
public LexicalScope getScopeForCompanionObjectHeaderResolution() {
return resolutionScopesSupport.getScopeForCompanionObjectHeaderResolution().invoke();
}
@Override
@NotNull
public LexicalScope getScopeForMemberDeclarationResolution() {
return resolutionScopesSupport.getScopeForMemberDeclarationResolution().invoke();
}
@Override
@NotNull
public LexicalScope getScopeForStaticMemberDeclarationResolution() {
return resolutionScopesSupport.getScopeForStaticMemberDeclarationResolution().invoke();
}
@Override
@NotNull
public LexicalScope getScopeForInitializerResolution() {
return scopeForInitializerResolution.invoke();
}
@NotNull
@Override
public Collection<CallableMemberDescriptor> getDeclaredCallableMembers() {
//noinspection unchecked
return (Collection) CollectionsKt.filter(
DescriptorUtils.getAllDescriptors(unsubstitutedMemberScope),
descriptor -> descriptor instanceof CallableMemberDescriptor
&& ((CallableMemberDescriptor) descriptor).getKind() != CallableMemberDescriptor.Kind.FAKE_OVERRIDE
);
}
@NotNull
@Override
public MemberScope getStaticScope() {
return staticScope;
}
@NotNull
@Override
public Collection<ClassConstructorDescriptor> getConstructors() {
return unsubstitutedMemberScope.getConstructors();
}
@Override
public ClassConstructorDescriptor getUnsubstitutedPrimaryConstructor() {
return unsubstitutedMemberScope.getPrimaryConstructor();
}
@NotNull
@Override
public TypeConstructor getTypeConstructor() {
return typeConstructor;
}
@Override
public ClassDescriptorWithResolutionScopes getCompanionObjectDescriptor() {
return companionObjectDescriptor.invoke();
}
@NotNull
@ReadOnly
public List<ClassDescriptor> getDescriptorsForExtraCompanionObjects() {
KtObjectDeclaration allowedCompanionObject = getCompanionObjectIfAllowed();
return CollectionsKt.map(
CollectionsKt.filter(
declarationProvider.getOwnerInfo().getCompanionObjects(),
companionObject -> companionObject != allowedCompanionObject
),
extraCompanionObjectDescriptors
);
}
@Nullable
private ClassDescriptorWithResolutionScopes computeCompanionObjectDescriptor(@Nullable KtObjectDeclaration companionObject) {
if (companionObject == null)
return createSyntheticCompanionObjectDescriptor();
KtClassLikeInfo companionObjectInfo = getCompanionObjectInfo(companionObject);
if (!(companionObjectInfo instanceof KtClassOrObjectInfo)) {
return null;
}
Name name = ((KtClassOrObjectInfo) companionObjectInfo).getName();
assert name != null;
getUnsubstitutedMemberScope().getContributedClassifier(name, NoLookupLocation.WHEN_GET_COMPANION_OBJECT);
ClassDescriptor companionObjectDescriptor = c.getTrace().get(BindingContext.CLASS, companionObject);
if (companionObjectDescriptor instanceof ClassDescriptorWithResolutionScopes) {
assert DescriptorUtils.isCompanionObject(companionObjectDescriptor) : "Not a companion object: " + companionObjectDescriptor;
return (ClassDescriptorWithResolutionScopes)companionObjectDescriptor;
}
else {
return null;
}
}
private ClassDescriptorWithResolutionScopes createSyntheticCompanionObjectDescriptor() {
Name syntheticCompanionName = c.getSyntheticResolveExtension().getSyntheticCompanionObjectNameIfNeeded(this);
if (syntheticCompanionName == null)
return null;
return new SyntheticClassOrObjectDescriptor(c,
/* parentClassOrObject= */ classOrObject,
this, syntheticCompanionName, getSource(),
/* outerScope= */ getOuterScope(),
Modality.FINAL, PUBLIC, ClassKind.OBJECT, true);
}
@Nullable
private static KtClassLikeInfo getCompanionObjectInfo(@Nullable KtObjectDeclaration companionObject) {
if (companionObject != null) {
return KtClassInfoUtil.createClassLikeInfo(companionObject);
}
return null;
}
@Nullable
private KtObjectDeclaration getCompanionObjectIfAllowed() {
KtObjectDeclaration companionObject = firstOrNull(declarationProvider.getOwnerInfo().getCompanionObjects());
return (companionObject != null && isCompanionObjectAllowed()) ? companionObject : null;
}
private boolean isCompanionObjectAllowed() {
return !(getKind().isSingleton() || isInner() || DescriptorUtils.isLocal(this));
}
@NotNull
@Override
public ClassKind getKind() {
return kind;
}
@NotNull
@Override
public Modality getModality() {
return modality.invoke();
}
@NotNull
@Override
public Visibility getVisibility() {
return visibility;
}
@Override
public boolean isInner() {
return isInner;
}
@Override
public boolean isData() {
return isData;
}
@Override
public boolean isCompanionObject() {
return isCompanionObject;
}
@Override
public boolean isHeader() {
return isHeader;
}
@Override
public boolean isImpl() {
return isImpl;
}
@NotNull
@Override
public Annotations getAnnotations() {
return annotations;
}
@NotNull
public Annotations getDanglingAnnotations() {
return danglingAnnotations;
}
@NotNull
@Override
public Collection<ClassDescriptor> getSealedSubclasses() {
return sealedSubclasses.invoke();
}
@Override
public String toString() {
// not using DescriptorRenderer to preserve laziness
return (isHeader ? "header " : isImpl ? "impl " : "") + "class " + getName().toString();
}
@Override
public void forceResolveAllContents() {
forceResolveAllContents.invoke();
}
private void doForceResolveAllContents() {
resolveMemberHeaders();
ClassDescriptor companionObjectDescriptor = getCompanionObjectDescriptor();
if (companionObjectDescriptor != null) {
ForceResolveUtil.forceResolveAllContents(companionObjectDescriptor);
}
ForceResolveUtil.forceResolveAllContents(getConstructors());
ForceResolveUtil.forceResolveAllContents(getDescriptorsForExtraCompanionObjects());
ForceResolveUtil.forceResolveAllContents(getUnsubstitutedMemberScope());
ForceResolveUtil.forceResolveAllContents(getTypeConstructor());
}
// Note: headers of member classes' members are not resolved
public void resolveMemberHeaders() {
ForceResolveUtil.forceResolveAllContents(getAnnotations());
ForceResolveUtil.forceResolveAllContents(getDanglingAnnotations());
getCompanionObjectDescriptor();
getDescriptorsForExtraCompanionObjects();
getConstructors();
getContainingDeclaration();
getThisAsReceiverParameter();
getKind();
getModality();
getName();
getOriginal();
getScopeForClassHeaderResolution();
getScopeForMemberDeclarationResolution();
DescriptorUtils.getAllDescriptors(getUnsubstitutedMemberScope());
getScopeForInitializerResolution();
getUnsubstitutedInnerClassesScope();
getTypeConstructor().getSupertypes();
for (TypeParameterDescriptor typeParameterDescriptor : getTypeConstructor().getParameters()) {
typeParameterDescriptor.getUpperBounds();
}
getUnsubstitutedPrimaryConstructor();
getVisibility();
}
@NotNull
@Override
public List<TypeParameterDescriptor> getDeclaredTypeParameters() {
return parameters.invoke();
}
private class LazyClassTypeConstructor extends AbstractClassTypeConstructor {
private final NotNullLazyValue<List<TypeParameterDescriptor>> parameters = c.getStorageManager().createLazyValue(
() -> TypeParameterUtilsKt.computeConstructorTypeParameters(LazyClassDescriptor.this)
);
public LazyClassTypeConstructor() {
super(LazyClassDescriptor.this.c.getStorageManager());
}
@NotNull
@Override
protected Collection<KotlinType> computeSupertypes() {
return LazyClassDescriptor.this.computeSupertypes();
}
@Override
protected void reportSupertypeLoopError(@NotNull KotlinType type) {
ClassifierDescriptor supertypeDescriptor = type.getConstructor().getDeclarationDescriptor();
if (supertypeDescriptor instanceof ClassDescriptor) {
ClassDescriptor superclass = (ClassDescriptor) supertypeDescriptor;
reportCyclicInheritanceHierarchyError(c.getTrace(), LazyClassDescriptor.this, superclass);
}
}
private void reportCyclicInheritanceHierarchyError(
@NotNull BindingTrace trace,
@NotNull ClassDescriptor classDescriptor,
@NotNull ClassDescriptor superclass
) {
PsiElement psiElement = DescriptorToSourceUtils.getSourceFromDescriptor(classDescriptor);
PsiElement elementToMark = null;
if (psiElement instanceof KtClassOrObject) {
KtClassOrObject classOrObject = (KtClassOrObject) psiElement;
for (KtSuperTypeListEntry delegationSpecifier : classOrObject.getSuperTypeListEntries()) {
KtTypeReference typeReference = delegationSpecifier.getTypeReference();
if (typeReference == null) continue;
KotlinType supertype = trace.get(TYPE, typeReference);
if (supertype != null && supertype.getConstructor() == superclass.getTypeConstructor()) {
elementToMark = typeReference;
}
}
}
if (elementToMark == null && psiElement instanceof PsiNameIdentifierOwner) {
PsiNameIdentifierOwner namedElement = (PsiNameIdentifierOwner) psiElement;
PsiElement nameIdentifier = namedElement.getNameIdentifier();
if (nameIdentifier != null) {
elementToMark = nameIdentifier;
}
}
if (elementToMark != null) {
trace.report(CYCLIC_INHERITANCE_HIERARCHY.on(elementToMark));
}
}
@NotNull
@Override
protected SupertypeLoopChecker getSupertypeLoopChecker() {
return c.getSupertypeLoopChecker();
}
@NotNull
@Override
public List<TypeParameterDescriptor> getParameters() {
return parameters.invoke();
}
@Override
public boolean isFinal() {
return getModality() == Modality.FINAL;
}
@Override
public boolean isDenotable() {
return true;
}
@Override
@NotNull
public ClassifierDescriptor getDeclarationDescriptor() {
return LazyClassDescriptor.this;
}
@Override
public String toString() {
return LazyClassDescriptor.this.getName().toString();
}
}
@NotNull
protected Collection<KotlinType> computeSupertypes() {
if (KotlinBuiltIns.isSpecialClassWithNoSupertypes(this)) {
return Collections.emptyList();
}
KtClassOrObject classOrObject = declarationProvider.getOwnerInfo().getCorrespondingClassOrObject();
if (classOrObject == null) {
return Collections.singleton(c.getModuleDescriptor().getBuiltIns().getAnyType());
}
List<KotlinType> allSupertypes =
c.getDescriptorResolver().resolveSupertypes(getScopeForClassHeaderResolution(), this, classOrObject, c.getTrace());
return new ArrayList<>(CollectionsKt.filter(allSupertypes, VALID_SUPERTYPE));
}
}