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