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