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