001    /*
002     * Copyright 2010-2014 JetBrains s.r.o.
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    package org.jetbrains.jet.lang.resolve;
018    
019    import kotlin.Function1;
020    import org.jetbrains.annotations.NotNull;
021    import org.jetbrains.annotations.Nullable;
022    import org.jetbrains.jet.lang.descriptors.*;
023    import org.jetbrains.jet.lang.descriptors.impl.AnonymousFunctionDescriptor;
024    import org.jetbrains.jet.lang.resolve.name.FqName;
025    import org.jetbrains.jet.lang.resolve.name.FqNameUnsafe;
026    import org.jetbrains.jet.lang.resolve.name.Name;
027    import org.jetbrains.jet.lang.resolve.name.SpecialNames;
028    import org.jetbrains.jet.lang.resolve.scopes.FilteringScope;
029    import org.jetbrains.jet.lang.resolve.scopes.JetScope;
030    import org.jetbrains.jet.lang.types.ErrorUtils;
031    import org.jetbrains.jet.lang.types.JetType;
032    import org.jetbrains.jet.lang.types.LazyType;
033    import org.jetbrains.jet.lang.types.TypeConstructor;
034    import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
035    
036    import java.util.ArrayList;
037    import java.util.Collection;
038    import java.util.List;
039    import java.util.Set;
040    
041    import static org.jetbrains.jet.lang.descriptors.ReceiverParameterDescriptor.NO_RECEIVER_PARAMETER;
042    
043    public class DescriptorUtils {
044        public static final Name ENUM_VALUES = Name.identifier("values");
045        public static final Name ENUM_VALUE_OF = Name.identifier("valueOf");
046    
047        private DescriptorUtils() {
048        }
049    
050        @Nullable
051        public static ReceiverParameterDescriptor getExpectedThisObjectIfNeeded(@NotNull DeclarationDescriptor containingDeclaration) {
052            if (containingDeclaration instanceof ClassDescriptor) {
053                ClassDescriptor classDescriptor = (ClassDescriptor) containingDeclaration;
054                return classDescriptor.getThisAsReceiverParameter();
055            }
056            else if (containingDeclaration instanceof ScriptDescriptor) {
057                ScriptDescriptor scriptDescriptor = (ScriptDescriptor) containingDeclaration;
058                return scriptDescriptor.getThisAsReceiverParameter();
059            }
060            return NO_RECEIVER_PARAMETER;
061        }
062    
063        /**
064         * Descriptor may be local itself or have a local ancestor
065         */
066        public static boolean isLocal(@NotNull DeclarationDescriptor descriptor) {
067            DeclarationDescriptor current = descriptor;
068            while (current instanceof MemberDescriptor) {
069                if (isAnonymousObject(current) || ((DeclarationDescriptorWithVisibility) current).getVisibility() == Visibilities.LOCAL) {
070                    return true;
071                }
072                current = current.getContainingDeclaration();
073            }
074            return false;
075        }
076    
077        @NotNull
078        public static FqNameUnsafe getFqName(@NotNull DeclarationDescriptor descriptor) {
079            FqName safe = getFqNameSafeIfPossible(descriptor);
080            return safe != null ? safe.toUnsafe() : getFqNameUnsafe(descriptor);
081        }
082    
083        @NotNull
084        public static FqName getFqNameSafe(@NotNull DeclarationDescriptor descriptor) {
085            FqName safe = getFqNameSafeIfPossible(descriptor);
086            return safe != null ? safe : getFqNameUnsafe(descriptor).toSafe();
087        }
088    
089    
090        @Nullable
091        private static FqName getFqNameSafeIfPossible(@NotNull DeclarationDescriptor descriptor) {
092            if (descriptor instanceof ModuleDescriptor || ErrorUtils.isError(descriptor)) {
093                return FqName.ROOT;
094            }
095    
096            if (descriptor instanceof PackageViewDescriptor) {
097                return ((PackageViewDescriptor) descriptor).getFqName();
098            }
099            else if (descriptor instanceof PackageFragmentDescriptor) {
100                return ((PackageFragmentDescriptor) descriptor).getFqName();
101            }
102    
103            return null;
104        }
105    
106        @NotNull
107        private static FqNameUnsafe getFqNameUnsafe(@NotNull DeclarationDescriptor descriptor) {
108            DeclarationDescriptor containingDeclaration = getContainingDeclarationSkippingClassObjects(descriptor);
109            assert containingDeclaration != null : "Not package/module descriptor doesn't have containing declaration: " + descriptor;
110            return getFqName(containingDeclaration).child(descriptor.getName());
111        }
112    
113        @Nullable
114        private static DeclarationDescriptor getContainingDeclarationSkippingClassObjects(@NotNull DeclarationDescriptor descriptor) {
115            DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
116            return isClassObject(containingDeclaration) ? containingDeclaration.getContainingDeclaration() : containingDeclaration;
117        }
118    
119        @NotNull
120        public static FqName getFqNameFromTopLevelClass(@NotNull DeclarationDescriptor descriptor) {
121            DeclarationDescriptor containingDeclaration = getContainingDeclarationSkippingClassObjects(descriptor);
122            Name name = descriptor.getName();
123            if (!(containingDeclaration instanceof ClassDescriptor)) {
124                return FqName.topLevel(name);
125            }
126            return getFqNameFromTopLevelClass(containingDeclaration).child(name);
127        }
128    
129        public static boolean isTopLevelDeclaration(@NotNull DeclarationDescriptor descriptor) {
130            return descriptor.getContainingDeclaration() instanceof PackageFragmentDescriptor;
131        }
132    
133        /**
134         * @return true iff this is a top-level declaration or a class member with no expected "this" object (e.g. static members in Java,
135         * values() and valueOf() methods of enum classes, etc.)
136         */
137        public static boolean isStaticDeclaration(@NotNull CallableDescriptor descriptor) {
138            if (descriptor instanceof ConstructorDescriptor) return false;
139    
140            DeclarationDescriptor container = descriptor.getContainingDeclaration();
141            return container instanceof PackageFragmentDescriptor ||
142                   (container instanceof ClassDescriptor && descriptor.getExpectedThisObject() == null);
143        }
144    
145        // WARNING! Don't use this method in JVM backend, use JvmCodegenUtil.isCallInsideSameModuleAsDeclared() instead.
146        // The latter handles compilation against compiled part of our module correctly.
147        public static boolean areInSameModule(@NotNull DeclarationDescriptor first, @NotNull DeclarationDescriptor second) {
148            return getContainingModule(first).equals(getContainingModule(second));
149        }
150    
151        @Nullable
152        public static <D extends DeclarationDescriptor> D getParentOfType(
153                @Nullable DeclarationDescriptor descriptor,
154                @NotNull Class<D> aClass
155        ) {
156            return getParentOfType(descriptor, aClass, true);
157        }
158    
159        @Nullable
160        public static <D extends DeclarationDescriptor> D getParentOfType(
161                @Nullable DeclarationDescriptor descriptor,
162                @NotNull Class<D> aClass,
163                boolean strict
164        ) {
165            if (descriptor == null) return null;
166            if (strict) {
167                descriptor = descriptor.getContainingDeclaration();
168            }
169            while (descriptor != null) {
170                if (aClass.isInstance(descriptor)) {
171                    //noinspection unchecked
172                    return (D) descriptor;
173                }
174                descriptor = descriptor.getContainingDeclaration();
175            }
176            return null;
177        }
178    
179        @NotNull
180        public static ModuleDescriptor getContainingModule(@NotNull DeclarationDescriptor descriptor) {
181            if (descriptor instanceof PackageViewDescriptor) {
182                return ((PackageViewDescriptor) descriptor).getModule();
183            }
184            ModuleDescriptor module = getParentOfType(descriptor, ModuleDescriptor.class, false);
185            assert module != null : "Descriptor without a containing module: " + descriptor;
186            return module;
187        }
188    
189        public static boolean isAncestor(
190                @Nullable DeclarationDescriptor ancestor,
191                @NotNull DeclarationDescriptor declarationDescriptor,
192                boolean strict
193        ) {
194            if (ancestor == null) return false;
195            DeclarationDescriptor descriptor = strict ? declarationDescriptor.getContainingDeclaration() : declarationDescriptor;
196            while (descriptor != null) {
197                if (ancestor == descriptor) return true;
198                descriptor = descriptor.getContainingDeclaration();
199            }
200            return false;
201        }
202    
203        public static boolean isSubclass(@NotNull ClassDescriptor subClass, @NotNull ClassDescriptor superClass) {
204            return isSubtypeOfClass(subClass.getDefaultType(), superClass.getOriginal());
205        }
206    
207        private static boolean isSubtypeOfClass(@NotNull JetType type, @NotNull DeclarationDescriptor superClass) {
208            DeclarationDescriptor descriptor = type.getConstructor().getDeclarationDescriptor();
209            if (descriptor != null) {
210                DeclarationDescriptor originalDescriptor = descriptor.getOriginal();
211                if (originalDescriptor instanceof ClassifierDescriptor
212                         && superClass instanceof ClassifierDescriptor
213                         && ((ClassifierDescriptor) superClass).getTypeConstructor().equals(((ClassifierDescriptor) originalDescriptor).getTypeConstructor())) {
214                    return true;
215                }
216            }
217    
218            for (JetType superType : type.getConstructor().getSupertypes()) {
219                if (isSubtypeOfClass(superType, superClass)) {
220                    return true;
221                }
222            }
223            return false;
224        }
225    
226        public static boolean isFunctionLiteral(@NotNull FunctionDescriptor descriptor) {
227            return descriptor instanceof AnonymousFunctionDescriptor;
228        }
229    
230        public static boolean isClassObject(@Nullable DeclarationDescriptor descriptor) {
231            return isKindOf(descriptor, ClassKind.CLASS_OBJECT);
232        }
233    
234        public static boolean isAnonymousObject(@NotNull DeclarationDescriptor descriptor) {
235            return isClass(descriptor) && descriptor.getName().equals(SpecialNames.NO_NAME_PROVIDED);
236        }
237    
238        public static boolean isObject(@NotNull DeclarationDescriptor descriptor) {
239            return isKindOf(descriptor, ClassKind.OBJECT);
240        }
241    
242        public static boolean isEnumEntry(@NotNull DeclarationDescriptor descriptor) {
243            return isKindOf(descriptor, ClassKind.ENUM_ENTRY);
244        }
245    
246        public static boolean isSingleton(@Nullable DeclarationDescriptor classifier) {
247            if (classifier instanceof ClassDescriptor) {
248                ClassDescriptor clazz = (ClassDescriptor) classifier;
249                return clazz.getKind().isSingleton();
250            }
251            return false;
252        }
253    
254        public static boolean isEnumClass(@Nullable DeclarationDescriptor descriptor) {
255            return isKindOf(descriptor, ClassKind.ENUM_CLASS);
256        }
257    
258        public static boolean isAnnotationClass(@Nullable DeclarationDescriptor descriptor) {
259            return isKindOf(descriptor, ClassKind.ANNOTATION_CLASS);
260        }
261    
262        public static boolean isTrait(@Nullable DeclarationDescriptor descriptor) {
263            return isKindOf(descriptor, ClassKind.TRAIT);
264        }
265    
266        public static boolean isClass(@Nullable DeclarationDescriptor descriptor) {
267            return isKindOf(descriptor, ClassKind.CLASS);
268        }
269    
270        public static boolean containerKindIs(@NotNull DeclarationDescriptor descriptor, @NotNull ClassKind kind) {
271            DeclarationDescriptor parentDeclaration = descriptor.getContainingDeclaration();
272            return  parentDeclaration != null && isKindOf(parentDeclaration, kind);
273        }
274    
275        public static boolean isKindOf(@Nullable DeclarationDescriptor descriptor, @NotNull ClassKind classKind) {
276            return descriptor instanceof ClassDescriptor && ((ClassDescriptor) descriptor).getKind() == classKind;
277        }
278    
279        @NotNull
280        public static List<ClassDescriptor> getSuperclassDescriptors(@NotNull ClassDescriptor classDescriptor) {
281            Collection<JetType> superclassTypes = classDescriptor.getTypeConstructor().getSupertypes();
282            List<ClassDescriptor> superClassDescriptors = new ArrayList<ClassDescriptor>();
283            for (JetType type : superclassTypes) {
284                ClassDescriptor result = getClassDescriptorForType(type);
285                if (!isAny(result)) {
286                    superClassDescriptors.add(result);
287                }
288            }
289            return superClassDescriptors;
290        }
291    
292        @NotNull
293        public static ClassDescriptor getClassDescriptorForType(@NotNull JetType type) {
294            return getClassDescriptorForTypeConstructor(type.getConstructor());
295        }
296    
297        @NotNull
298        public static ClassDescriptor getClassDescriptorForTypeConstructor(@NotNull TypeConstructor typeConstructor) {
299            ClassifierDescriptor descriptor = typeConstructor.getDeclarationDescriptor();
300            assert descriptor instanceof ClassDescriptor
301                : "Classifier descriptor of a type should be of type ClassDescriptor: " + typeConstructor;
302            return (ClassDescriptor) descriptor;
303        }
304    
305        public static boolean isAny(@NotNull DeclarationDescriptor superClassDescriptor) {
306            return superClassDescriptor.equals(KotlinBuiltIns.getInstance().getAny());
307        }
308    
309        public static boolean isSyntheticClassObject(@NotNull DeclarationDescriptor descriptor) {
310            return isClassObject(descriptor) && isSingleton(descriptor.getContainingDeclaration());
311        }
312    
313        @NotNull
314        public static Visibility getDefaultConstructorVisibility(@NotNull ClassDescriptor classDescriptor) {
315            ClassKind classKind = classDescriptor.getKind();
316            if (classKind == ClassKind.ENUM_CLASS || classKind.isSingleton()) {
317                return Visibilities.PRIVATE;
318            }
319            if (isAnonymousObject(classDescriptor)) {
320                return Visibilities.INTERNAL;
321            }
322            assert classKind == ClassKind.CLASS || classKind == ClassKind.TRAIT || classKind == ClassKind.ANNOTATION_CLASS;
323            return Visibilities.PUBLIC;
324        }
325    
326        @NotNull
327        public static Visibility getSyntheticClassObjectVisibility() {
328            return Visibilities.PUBLIC;
329        }
330    
331        @Nullable
332        public static ClassDescriptor getInnerClassByName(@NotNull ClassDescriptor classDescriptor, @NotNull String innerClassName) {
333            ClassifierDescriptor classifier = classDescriptor.getDefaultType().getMemberScope().getClassifier(Name.identifier(innerClassName));
334            assert classifier instanceof ClassDescriptor :
335                    "Inner class " + innerClassName + " in " + classDescriptor + " should be instance of ClassDescriptor, but was: "
336                    + (classifier == null ? "null" : classifier.getClass());
337            return (ClassDescriptor) classifier;
338        }
339    
340        @Nullable
341        public static JetType getReceiverParameterType(@Nullable ReceiverParameterDescriptor receiverParameterDescriptor) {
342            return receiverParameterDescriptor == null ? null : receiverParameterDescriptor.getType();
343        }
344    
345        /**
346         * @return true if descriptor is a class inside another class and does not have access to the outer class
347         */
348        public static boolean isStaticNestedClass(@NotNull DeclarationDescriptor descriptor) {
349            DeclarationDescriptor containing = descriptor.getContainingDeclaration();
350            return descriptor instanceof ClassDescriptor &&
351                   containing instanceof ClassDescriptor &&
352                   !((ClassDescriptor) descriptor).isInner();
353        }
354    
355        @NotNull
356        public static JetScope getStaticNestedClassesScope(@NotNull ClassDescriptor descriptor) {
357            JetScope innerClassesScope = descriptor.getUnsubstitutedInnerClassesScope();
358            return new FilteringScope(innerClassesScope, new Function1<DeclarationDescriptor, Boolean>() {
359                @Override
360                public Boolean invoke(DeclarationDescriptor descriptor) {
361                    return descriptor instanceof ClassDescriptor && !((ClassDescriptor) descriptor).isInner();
362                }
363            });
364        }
365    
366        /**
367         * @return true iff {@code descriptor}'s first non-class container is a package
368         */
369        public static boolean isTopLevelOrInnerClass(@NotNull ClassDescriptor descriptor) {
370            DeclarationDescriptor containing = descriptor.getContainingDeclaration();
371            return isTopLevelDeclaration(descriptor) ||
372                   containing instanceof ClassDescriptor && isTopLevelOrInnerClass((ClassDescriptor) containing);
373        }
374    
375        /**
376         * Given a fake override, finds any declaration of it in the overridden descriptors. Keep in mind that there may be many declarations
377         * of the fake override in the supertypes, this method finds just the only one.
378         * TODO: probably all call-sites of this method are wrong, they should handle all super-declarations
379         */
380        @NotNull
381        public static <D extends CallableMemberDescriptor> D unwrapFakeOverride(@NotNull D descriptor) {
382            while (descriptor.getKind() == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) {
383                Set<? extends CallableMemberDescriptor> overridden = descriptor.getOverriddenDescriptors();
384                if (overridden.isEmpty()) {
385                    throw new IllegalStateException("Fake override should have at least one overridden descriptor: " + descriptor);
386                }
387                //noinspection unchecked
388                descriptor = (D) overridden.iterator().next();
389            }
390            return descriptor;
391        }
392    
393        public static boolean shouldRecordInitializerForProperty(@NotNull VariableDescriptor variable, @NotNull JetType type) {
394            if (variable.isVar() || type.isError()) return false;
395    
396            if (type instanceof LazyType || type.isNullable()) return true;
397    
398            KotlinBuiltIns builtIns = KotlinBuiltIns.getInstance();
399            return builtIns.isPrimitiveType(type) ||
400                   builtIns.getStringType().equals(type) ||
401                   builtIns.getNumber().getDefaultType().equals(type) ||
402                   builtIns.getAnyType().equals(type);
403        }
404    }