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