001    /*
002     * Copyright 2010-2013 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 com.google.common.base.Predicate;
020    import com.google.common.collect.Lists;
021    import com.google.common.collect.Sets;
022    import org.jetbrains.annotations.NotNull;
023    import org.jetbrains.annotations.Nullable;
024    import org.jetbrains.jet.lang.descriptors.*;
025    import org.jetbrains.jet.lang.descriptors.impl.AnonymousFunctionDescriptor;
026    import org.jetbrains.jet.lang.resolve.name.FqName;
027    import org.jetbrains.jet.lang.resolve.name.FqNameUnsafe;
028    import org.jetbrains.jet.lang.resolve.name.Name;
029    import org.jetbrains.jet.lang.resolve.name.SpecialNames;
030    import org.jetbrains.jet.lang.resolve.scopes.FilteringScope;
031    import org.jetbrains.jet.lang.resolve.scopes.JetScope;
032    import org.jetbrains.jet.lang.types.*;
033    import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
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        private DescriptorUtils() {
045        }
046    
047        @NotNull
048        public static <D extends CallableDescriptor> D substituteBounds(@NotNull D functionDescriptor) {
049            List<TypeParameterDescriptor> typeParameters = functionDescriptor.getTypeParameters();
050            if (typeParameters.isEmpty()) return functionDescriptor;
051    
052            // TODO: this does not handle any recursion in the bounds
053            @SuppressWarnings("unchecked")
054            D substitutedFunction = (D) functionDescriptor.substitute(DescriptorSubstitutor.createUpperBoundsSubstitutor(typeParameters));
055            assert substitutedFunction != null : "Substituting upper bounds should always be legal";
056    
057            return substitutedFunction;
058        }
059    
060        @NotNull
061        public static Modality convertModality(@NotNull Modality modality, boolean makeNonAbstract) {
062            if (makeNonAbstract && modality == Modality.ABSTRACT) return Modality.OPEN;
063            return modality;
064        }
065    
066        @Nullable
067        public static ReceiverParameterDescriptor getExpectedThisObjectIfNeeded(@NotNull DeclarationDescriptor containingDeclaration) {
068            if (containingDeclaration instanceof ClassDescriptor) {
069                ClassDescriptor classDescriptor = (ClassDescriptor) containingDeclaration;
070                return classDescriptor.getThisAsReceiverParameter();
071            }
072            else if (containingDeclaration instanceof ScriptDescriptor) {
073                ScriptDescriptor scriptDescriptor = (ScriptDescriptor) containingDeclaration;
074                return scriptDescriptor.getThisAsReceiverParameter();
075            }
076            return NO_RECEIVER_PARAMETER;
077        }
078    
079        /**
080         * The primary case for local extensions is the following:
081         *
082         * I had a locally declared extension function or a local variable of function type called foo
083         * And I called it on my x
084         * Now, someone added function foo() to the class of x
085         * My code should not change
086         *
087         * thus
088         *
089         * local extension prevail over members (and members prevail over all non-local extensions)
090         */
091        public static boolean isLocal(DeclarationDescriptor containerOfTheCurrentLocality, DeclarationDescriptor candidate) {
092            if (candidate instanceof ValueParameterDescriptor) {
093                return true;
094            }
095            DeclarationDescriptor parent = candidate.getContainingDeclaration();
096            if (!(parent instanceof FunctionDescriptor)) {
097                return false;
098            }
099            FunctionDescriptor functionDescriptor = (FunctionDescriptor) parent;
100            DeclarationDescriptor current = containerOfTheCurrentLocality;
101            while (current != null) {
102                if (current == functionDescriptor) {
103                    return true;
104                }
105                current = current.getContainingDeclaration();
106            }
107            return false;
108        }
109    
110        @NotNull
111        public static FqNameUnsafe getFqName(@NotNull DeclarationDescriptor descriptor) {
112            FqName safe = getFqNameSafeIfPossible(descriptor);
113            return safe != null ? safe.toUnsafe() : getFqNameUnsafe(descriptor);
114        }
115    
116        @NotNull
117        public static FqName getFqNameSafe(@NotNull DeclarationDescriptor descriptor) {
118            FqName safe = getFqNameSafeIfPossible(descriptor);
119            return safe != null ? safe : getFqNameUnsafe(descriptor).toSafe();
120        }
121    
122    
123        @Nullable
124        private static FqName getFqNameSafeIfPossible(@NotNull DeclarationDescriptor descriptor) {
125            if (descriptor instanceof ModuleDescriptor || ErrorUtils.isError(descriptor)) {
126                return FqName.ROOT;
127            }
128    
129            if (descriptor instanceof PackageViewDescriptor) {
130                return ((PackageViewDescriptor) descriptor).getFqName();
131            }
132            else if (descriptor instanceof PackageFragmentDescriptor) {
133                return ((PackageFragmentDescriptor) descriptor).getFqName();
134            }
135    
136            return null;
137        }
138    
139        @NotNull
140        private static FqNameUnsafe getFqNameUnsafe(@NotNull DeclarationDescriptor descriptor) {
141            DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
142    
143            if (containingDeclaration instanceof ClassDescriptor && ((ClassDescriptor) containingDeclaration).getKind() == ClassKind.CLASS_OBJECT) {
144                DeclarationDescriptor classOfClassObject = containingDeclaration.getContainingDeclaration();
145                assert classOfClassObject != null;
146                return getFqName(classOfClassObject).child(descriptor.getName());
147            }
148    
149            return getFqName(containingDeclaration).child(descriptor.getName());
150        }
151    
152        public static boolean isTopLevelDeclaration(@NotNull DeclarationDescriptor descriptor) {
153            return descriptor.getContainingDeclaration() instanceof PackageFragmentDescriptor;
154        }
155    
156        public static boolean areInSameModule(@NotNull DeclarationDescriptor first, @NotNull DeclarationDescriptor second) {
157            ModuleDescriptor parentModule = getParentOfType(first, ModuleDescriptor.class, false);
158            ModuleDescriptor fromModule = getParentOfType(second, ModuleDescriptor.class, false);
159            assert parentModule != null && fromModule != null;
160            return parentModule.equals(fromModule);
161        }
162    
163        @Nullable
164        public static DeclarationDescriptor findTopLevelParent(@NotNull DeclarationDescriptor declarationDescriptor) {
165            DeclarationDescriptor descriptor = declarationDescriptor;
166            if (declarationDescriptor instanceof PropertyAccessorDescriptor) {
167                descriptor = ((PropertyAccessorDescriptor) descriptor).getCorrespondingProperty();
168            }
169            while (!(descriptor == null || isTopLevelDeclaration(descriptor))) {
170                descriptor = descriptor.getContainingDeclaration();
171            }
172            return descriptor;
173        }
174    
175        @Nullable
176        public static <D extends DeclarationDescriptor> D getParentOfType(
177                @Nullable DeclarationDescriptor descriptor,
178                @NotNull Class<D> aClass
179        ) {
180            return getParentOfType(descriptor, aClass, true);
181        }
182    
183        @Nullable
184        public static <D extends DeclarationDescriptor> D getParentOfType(
185                @Nullable DeclarationDescriptor descriptor,
186                @NotNull Class<D> aClass,
187                boolean strict
188        ) {
189            if (descriptor == null) return null;
190            if (strict) {
191                descriptor = descriptor.getContainingDeclaration();
192            }
193            while (descriptor != null) {
194                if (aClass.isInstance(descriptor)) {
195                    //noinspection unchecked
196                    return (D) descriptor;
197                }
198                descriptor = descriptor.getContainingDeclaration();
199            }
200            return null;
201        }
202    
203        public static boolean isAncestor(
204                @Nullable DeclarationDescriptor ancestor,
205                @NotNull DeclarationDescriptor declarationDescriptor,
206                boolean strict
207        ) {
208            if (ancestor == null) return false;
209            DeclarationDescriptor descriptor = strict ? declarationDescriptor.getContainingDeclaration() : declarationDescriptor;
210            while (descriptor != null) {
211                if (ancestor == descriptor) return true;
212                descriptor = descriptor.getContainingDeclaration();
213            }
214            return false;
215        }
216    
217        public static boolean isSubclass(@NotNull ClassDescriptor subClass, @NotNull ClassDescriptor superClass) {
218            return isSubtypeOfClass(subClass.getDefaultType(), superClass.getOriginal());
219        }
220    
221        private static boolean isSubtypeOfClass(@NotNull JetType type, @NotNull DeclarationDescriptor superClass) {
222            DeclarationDescriptor descriptor = type.getConstructor().getDeclarationDescriptor();
223            if (descriptor != null && superClass == descriptor.getOriginal()) {
224                return true;
225            }
226            for (JetType superType : type.getConstructor().getSupertypes()) {
227                if (isSubtypeOfClass(superType, superClass)) {
228                    return true;
229                }
230            }
231            return false;
232        }
233    
234        public static boolean isFunctionLiteral(@NotNull FunctionDescriptor descriptor) {
235            return descriptor instanceof AnonymousFunctionDescriptor;
236        }
237    
238        public static boolean isClassObject(@NotNull DeclarationDescriptor descriptor) {
239            return isKindOf(descriptor, ClassKind.CLASS_OBJECT);
240        }
241    
242        public static boolean isAnonymousObject(@NotNull DeclarationDescriptor descriptor) {
243            return isClass(descriptor) && descriptor.getName().equals(SpecialNames.NO_NAME_PROVIDED);
244        }
245    
246        public static boolean isObject(@NotNull DeclarationDescriptor descriptor) {
247            return isKindOf(descriptor, ClassKind.OBJECT);
248        }
249    
250        public static boolean isEnumEntry(@NotNull DeclarationDescriptor descriptor) {
251            return isKindOf(descriptor, ClassKind.ENUM_ENTRY);
252        }
253    
254        public static boolean isSingleton(@Nullable DeclarationDescriptor classifier) {
255            if (classifier instanceof ClassDescriptor) {
256                ClassDescriptor clazz = (ClassDescriptor) classifier;
257                return clazz.getKind().isSingleton();
258            }
259            return false;
260        }
261    
262        public static boolean isEnumClass(@NotNull DeclarationDescriptor descriptor) {
263            return isKindOf(descriptor, ClassKind.ENUM_CLASS);
264        }
265    
266        public static boolean isAnnotationClass(@Nullable DeclarationDescriptor descriptor) {
267            return isKindOf(descriptor, ClassKind.ANNOTATION_CLASS);
268        }
269    
270        public static boolean isTrait(@NotNull DeclarationDescriptor descriptor) {
271            return isKindOf(descriptor, ClassKind.TRAIT);
272        }
273    
274        public static boolean isClass(@NotNull DeclarationDescriptor descriptor) {
275            return isKindOf(descriptor, ClassKind.CLASS);
276        }
277    
278        public static boolean isKindOf(@Nullable DeclarationDescriptor descriptor, @NotNull ClassKind classKind) {
279            return descriptor instanceof ClassDescriptor && ((ClassDescriptor) descriptor).getKind() == classKind;
280        }
281    
282        @NotNull
283        public static List<ClassDescriptor> getSuperclassDescriptors(@NotNull ClassDescriptor classDescriptor) {
284            Collection<JetType> superclassTypes = classDescriptor.getTypeConstructor().getSupertypes();
285            List<ClassDescriptor> superClassDescriptors = new ArrayList<ClassDescriptor>();
286            for (JetType type : superclassTypes) {
287                ClassDescriptor result = getClassDescriptorForType(type);
288                if (!isAny(result)) {
289                    superClassDescriptors.add(result);
290                }
291            }
292            return superClassDescriptors;
293        }
294    
295        @NotNull
296        public static ClassDescriptor getClassDescriptorForType(@NotNull JetType type) {
297            return getClassDescriptorForTypeConstructor(type.getConstructor());
298        }
299    
300        @NotNull
301        public static ClassDescriptor getClassDescriptorForTypeConstructor(@NotNull TypeConstructor typeConstructor) {
302            ClassifierDescriptor descriptor = typeConstructor.getDeclarationDescriptor();
303            assert descriptor instanceof ClassDescriptor
304                : "Classifier descriptor of a type should be of type ClassDescriptor: " + typeConstructor;
305            return (ClassDescriptor) descriptor;
306        }
307    
308        public static boolean isAny(@NotNull DeclarationDescriptor superClassDescriptor) {
309            return superClassDescriptor.equals(KotlinBuiltIns.getInstance().getAny());
310        }
311    
312        public static boolean isEnumClassObject(@NotNull DeclarationDescriptor descriptor) {
313            if (descriptor instanceof ClassDescriptor && ((ClassDescriptor) descriptor).getKind() == ClassKind.CLASS_OBJECT) {
314                DeclarationDescriptor containing = descriptor.getContainingDeclaration();
315                if ((containing instanceof ClassDescriptor) && ((ClassDescriptor) containing).getKind() == ClassKind.ENUM_CLASS) {
316                    return true;
317                }
318            }
319            return false;
320        }
321    
322        public static boolean isSyntheticClassObject(@NotNull DeclarationDescriptor descriptor) {
323            if (isClassObject(descriptor)) {
324                DeclarationDescriptor containing = descriptor.getContainingDeclaration();
325                if (containing != null) {
326                    return isEnumClass(containing) || isObject(containing) || isEnumEntry(containing);
327                }
328            }
329            return false;
330        }
331    
332        @NotNull
333        public static Visibility getDefaultConstructorVisibility(@NotNull ClassDescriptor classDescriptor) {
334            ClassKind classKind = classDescriptor.getKind();
335            if (classKind == ClassKind.ENUM_CLASS || classKind.isSingleton() || isAnonymousObject(classDescriptor)) {
336                return Visibilities.PRIVATE;
337            }
338            assert classKind == ClassKind.CLASS || classKind == ClassKind.TRAIT || classKind == ClassKind.ANNOTATION_CLASS;
339            return Visibilities.PUBLIC;
340        }
341    
342        @NotNull
343        public static Visibility getSyntheticClassObjectVisibility() {
344            return Visibilities.PUBLIC;
345        }
346    
347        @Nullable
348        public static ClassDescriptor getInnerClassByName(@NotNull ClassDescriptor classDescriptor, @NotNull String innerClassName) {
349            ClassifierDescriptor classifier = classDescriptor.getDefaultType().getMemberScope().getClassifier(Name.identifier(innerClassName));
350            assert classifier instanceof ClassDescriptor :
351                    "Inner class " + innerClassName + " in " + classDescriptor + " should be instance of ClassDescriptor, but was: "
352                    + (classifier == null ? "null" : classifier.getClass());
353            return (ClassDescriptor) classifier;
354        }
355    
356        @NotNull
357        public static ConstructorDescriptor getConstructorOfDataClass(ClassDescriptor classDescriptor) {
358            ConstructorDescriptor descriptor = getConstructorDescriptorIfOnlyOne(classDescriptor);
359            assert descriptor != null : "Data class must have only one constructor: " + classDescriptor.getConstructors();
360            return descriptor;
361        }
362    
363        @NotNull
364        public static ConstructorDescriptor getConstructorOfSingletonObject(ClassDescriptor classDescriptor) {
365            ConstructorDescriptor descriptor = getConstructorDescriptorIfOnlyOne(classDescriptor);
366            assert descriptor != null : "Class of singleton object must have only one constructor: " + classDescriptor.getConstructors();
367            return descriptor;
368        }
369    
370        @Nullable
371        private static ConstructorDescriptor getConstructorDescriptorIfOnlyOne(ClassDescriptor classDescriptor) {
372            Collection<ConstructorDescriptor> constructors = classDescriptor.getConstructors();
373            return constructors.size() != 1 ? null : constructors.iterator().next();
374        }
375    
376        @Nullable
377        public static JetType getReceiverParameterType(@Nullable ReceiverParameterDescriptor receiverParameterDescriptor) {
378            if (receiverParameterDescriptor == null) {
379                return null;
380            }
381            return receiverParameterDescriptor.getType();
382        }
383    
384        @NotNull
385        public static JetType getVarargParameterType(@NotNull JetType elementType) {
386            JetType primitiveArrayType = KotlinBuiltIns.getInstance().getPrimitiveArrayJetTypeByPrimitiveJetType(elementType);
387            return primitiveArrayType != null ? primitiveArrayType : KotlinBuiltIns.getInstance().getArrayType(Variance.INVARIANT, elementType);
388        }
389    
390        @NotNull
391        public static List<JetType> getValueParametersTypes(@NotNull List<ValueParameterDescriptor> valueParameters) {
392            List<JetType> parameterTypes = Lists.newArrayList();
393            for (ValueParameterDescriptor parameter : valueParameters) {
394                parameterTypes.add(parameter.getType());
395            }
396            return parameterTypes;
397        }
398    
399        public static boolean isInsideOuterClassOrItsSubclass(@Nullable DeclarationDescriptor nested, @NotNull ClassDescriptor outer) {
400            if (nested == null) return false;
401    
402            if (nested instanceof ClassDescriptor && isSubclass((ClassDescriptor) nested, outer)) return true;
403    
404            return isInsideOuterClassOrItsSubclass(nested.getContainingDeclaration(), outer);
405        }
406    
407        public static boolean isConstructorOfStaticNestedClass(@Nullable CallableDescriptor descriptor) {
408            return descriptor instanceof ConstructorDescriptor && isStaticNestedClass(descriptor.getContainingDeclaration());
409        }
410    
411        /**
412         * @return true if descriptor is a class inside another class and does not have access to the outer class
413         */
414        public static boolean isStaticNestedClass(@NotNull DeclarationDescriptor descriptor) {
415            DeclarationDescriptor containing = descriptor.getContainingDeclaration();
416            return descriptor instanceof ClassDescriptor &&
417                   containing instanceof ClassDescriptor &&
418                   !((ClassDescriptor) descriptor).isInner();
419        }
420    
421        @Nullable
422        public static ClassDescriptor getContainingClass(@NotNull JetScope scope) {
423            DeclarationDescriptor containingDeclaration = scope.getContainingDeclaration();
424            return getParentOfType(containingDeclaration, ClassDescriptor.class, false);
425        }
426    
427        @NotNull
428        public static JetScope getStaticNestedClassesScope(@NotNull ClassDescriptor descriptor) {
429            JetScope innerClassesScope = descriptor.getUnsubstitutedInnerClassesScope();
430            return new FilteringScope(innerClassesScope, new Predicate<DeclarationDescriptor>() {
431                @Override
432                public boolean apply(@Nullable DeclarationDescriptor descriptor) {
433                    return descriptor instanceof ClassDescriptor && !((ClassDescriptor) descriptor).isInner();
434                }
435            });
436        }
437    
438        public static boolean isEnumValueOfMethod(@NotNull FunctionDescriptor functionDescriptor) {
439            List<ValueParameterDescriptor> methodTypeParameters = functionDescriptor.getValueParameters();
440            JetType nullableString = TypeUtils.makeNullable(KotlinBuiltIns.getInstance().getStringType());
441            return "valueOf".equals(functionDescriptor.getName().asString())
442                   && methodTypeParameters.size() == 1
443                   && JetTypeChecker.INSTANCE.isSubtypeOf(methodTypeParameters.get(0).getType(), nullableString);
444        }
445    
446        public static boolean isEnumValuesMethod(@NotNull FunctionDescriptor functionDescriptor) {
447            List<ValueParameterDescriptor> methodTypeParameters = functionDescriptor.getValueParameters();
448            return "values".equals(functionDescriptor.getName().asString())
449                   && methodTypeParameters.isEmpty();
450        }
451    
452        @NotNull
453        public static Set<ClassDescriptor> getAllSuperClasses(@NotNull ClassDescriptor klass) {
454            Set<JetType> allSupertypes = TypeUtils.getAllSupertypes(klass.getDefaultType());
455            Set<ClassDescriptor> allSuperclasses = Sets.newHashSet();
456            for (JetType supertype : allSupertypes) {
457                ClassDescriptor superclass = TypeUtils.getClassDescriptor(supertype);
458                assert superclass != null;
459                allSuperclasses.add(superclass);
460            }
461            return allSuperclasses;
462        }
463    
464        /**
465         * @return true iff {@code descriptor}'s first non-class container is a package
466         */
467        public static boolean isTopLevelOrInnerClass(@NotNull ClassDescriptor descriptor) {
468            DeclarationDescriptor containing = descriptor.getContainingDeclaration();
469            return isTopLevelDeclaration(descriptor) ||
470                   containing instanceof ClassDescriptor && isTopLevelOrInnerClass((ClassDescriptor) containing);
471        }
472    }