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 org.jetbrains.annotations.NotNull;
021    import org.jetbrains.annotations.Nullable;
022    import org.jetbrains.jet.lang.descriptors.*;
023    import org.jetbrains.jet.lang.descriptors.impl.AnonymousFunctionDescriptor;
024    import org.jetbrains.jet.lang.resolve.name.FqName;
025    import org.jetbrains.jet.lang.resolve.name.FqNameUnsafe;
026    import org.jetbrains.jet.lang.resolve.name.Name;
027    import org.jetbrains.jet.lang.resolve.name.SpecialNames;
028    import org.jetbrains.jet.lang.resolve.scopes.FilteringScope;
029    import org.jetbrains.jet.lang.resolve.scopes.JetScope;
030    import org.jetbrains.jet.lang.types.*;
031    import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
032    import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
033    
034    import java.util.ArrayList;
035    import java.util.Collection;
036    import java.util.List;
037    import java.util.Set;
038    
039    import static org.jetbrains.jet.lang.descriptors.ReceiverParameterDescriptor.NO_RECEIVER_PARAMETER;
040    
041    public class DescriptorUtils {
042        private DescriptorUtils() {
043        }
044    
045        @Nullable
046        public static ReceiverParameterDescriptor getExpectedThisObjectIfNeeded(@NotNull DeclarationDescriptor containingDeclaration) {
047            if (containingDeclaration instanceof ClassDescriptor) {
048                ClassDescriptor classDescriptor = (ClassDescriptor) containingDeclaration;
049                return classDescriptor.getThisAsReceiverParameter();
050            }
051            else if (containingDeclaration instanceof ScriptDescriptor) {
052                ScriptDescriptor scriptDescriptor = (ScriptDescriptor) containingDeclaration;
053                return scriptDescriptor.getThisAsReceiverParameter();
054            }
055            return NO_RECEIVER_PARAMETER;
056        }
057    
058        /**
059         * Descriptor may be local itself or have a local ancestor
060         */
061        public static boolean isLocal(@NotNull DeclarationDescriptor descriptor) {
062            DeclarationDescriptor current = descriptor;
063            while (current instanceof MemberDescriptor) {
064                if (isAnonymousObject(current) || ((DeclarationDescriptorWithVisibility) current).getVisibility() == Visibilities.LOCAL) {
065                    return true;
066                }
067                current = current.getContainingDeclaration();
068            }
069            return false;
070        }
071    
072        @NotNull
073        public static FqNameUnsafe getFqName(@NotNull DeclarationDescriptor descriptor) {
074            FqName safe = getFqNameSafeIfPossible(descriptor);
075            return safe != null ? safe.toUnsafe() : getFqNameUnsafe(descriptor);
076        }
077    
078        @NotNull
079        public static FqName getFqNameSafe(@NotNull DeclarationDescriptor descriptor) {
080            FqName safe = getFqNameSafeIfPossible(descriptor);
081            return safe != null ? safe : getFqNameUnsafe(descriptor).toSafe();
082        }
083    
084    
085        @Nullable
086        private static FqName getFqNameSafeIfPossible(@NotNull DeclarationDescriptor descriptor) {
087            if (descriptor instanceof ModuleDescriptor || ErrorUtils.isError(descriptor)) {
088                return FqName.ROOT;
089            }
090    
091            if (descriptor instanceof PackageViewDescriptor) {
092                return ((PackageViewDescriptor) descriptor).getFqName();
093            }
094            else if (descriptor instanceof PackageFragmentDescriptor) {
095                return ((PackageFragmentDescriptor) descriptor).getFqName();
096            }
097    
098            return null;
099        }
100    
101        @NotNull
102        private static FqNameUnsafe getFqNameUnsafe(@NotNull DeclarationDescriptor descriptor) {
103            DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
104    
105            if (containingDeclaration instanceof ClassDescriptor && ((ClassDescriptor) containingDeclaration).getKind() == ClassKind.CLASS_OBJECT) {
106                DeclarationDescriptor classOfClassObject = containingDeclaration.getContainingDeclaration();
107                assert classOfClassObject != null;
108                return getFqName(classOfClassObject).child(descriptor.getName());
109            }
110    
111            return getFqName(containingDeclaration).child(descriptor.getName());
112        }
113    
114        public static boolean isTopLevelDeclaration(@NotNull DeclarationDescriptor descriptor) {
115            return descriptor.getContainingDeclaration() instanceof PackageFragmentDescriptor;
116        }
117    
118        public static boolean areInSameModule(@NotNull DeclarationDescriptor first, @NotNull DeclarationDescriptor second) {
119            return getContainingModule(first).equals(getContainingModule(second));
120        }
121    
122        @Nullable
123        public static <D extends DeclarationDescriptor> D getParentOfType(
124                @Nullable DeclarationDescriptor descriptor,
125                @NotNull Class<D> aClass
126        ) {
127            return getParentOfType(descriptor, aClass, true);
128        }
129    
130        @Nullable
131        public static <D extends DeclarationDescriptor> D getParentOfType(
132                @Nullable DeclarationDescriptor descriptor,
133                @NotNull Class<D> aClass,
134                boolean strict
135        ) {
136            if (descriptor == null) return null;
137            if (strict) {
138                descriptor = descriptor.getContainingDeclaration();
139            }
140            while (descriptor != null) {
141                if (aClass.isInstance(descriptor)) {
142                    //noinspection unchecked
143                    return (D) descriptor;
144                }
145                descriptor = descriptor.getContainingDeclaration();
146            }
147            return null;
148        }
149    
150        @NotNull
151        public static ModuleDescriptor getContainingModule(@NotNull DeclarationDescriptor descriptor) {
152            ModuleDescriptor module = getParentOfType(descriptor, ModuleDescriptor.class, false);
153            assert module != null : "Descriptor without a containing module: " + descriptor;
154            return module;
155        }
156    
157        public static boolean isAncestor(
158                @Nullable DeclarationDescriptor ancestor,
159                @NotNull DeclarationDescriptor declarationDescriptor,
160                boolean strict
161        ) {
162            if (ancestor == null) return false;
163            DeclarationDescriptor descriptor = strict ? declarationDescriptor.getContainingDeclaration() : declarationDescriptor;
164            while (descriptor != null) {
165                if (ancestor == descriptor) return true;
166                descriptor = descriptor.getContainingDeclaration();
167            }
168            return false;
169        }
170    
171        public static boolean isSubclass(@NotNull ClassDescriptor subClass, @NotNull ClassDescriptor superClass) {
172            return isSubtypeOfClass(subClass.getDefaultType(), superClass.getOriginal());
173        }
174    
175        private static boolean isSubtypeOfClass(@NotNull JetType type, @NotNull DeclarationDescriptor superClass) {
176            DeclarationDescriptor descriptor = type.getConstructor().getDeclarationDescriptor();
177            if (descriptor != null) {
178                DeclarationDescriptor originalDescriptor = descriptor.getOriginal();
179                if (originalDescriptor instanceof ClassifierDescriptor
180                         && superClass instanceof ClassifierDescriptor
181                         && ((ClassifierDescriptor) superClass).getTypeConstructor().equals(((ClassifierDescriptor) originalDescriptor).getTypeConstructor())) {
182                    return true;
183                }
184            }
185    
186            for (JetType superType : type.getConstructor().getSupertypes()) {
187                if (isSubtypeOfClass(superType, superClass)) {
188                    return true;
189                }
190            }
191            return false;
192        }
193    
194        public static boolean isFunctionLiteral(@NotNull FunctionDescriptor descriptor) {
195            return descriptor instanceof AnonymousFunctionDescriptor;
196        }
197    
198        public static boolean isClassObject(@NotNull DeclarationDescriptor descriptor) {
199            return isKindOf(descriptor, ClassKind.CLASS_OBJECT);
200        }
201    
202        public static boolean isAnonymousObject(@NotNull DeclarationDescriptor descriptor) {
203            return isClass(descriptor) && descriptor.getName().equals(SpecialNames.NO_NAME_PROVIDED);
204        }
205    
206        public static boolean isObject(@NotNull DeclarationDescriptor descriptor) {
207            return isKindOf(descriptor, ClassKind.OBJECT);
208        }
209    
210        public static boolean isEnumEntry(@NotNull DeclarationDescriptor descriptor) {
211            return isKindOf(descriptor, ClassKind.ENUM_ENTRY);
212        }
213    
214        public static boolean isSingleton(@Nullable DeclarationDescriptor classifier) {
215            if (classifier instanceof ClassDescriptor) {
216                ClassDescriptor clazz = (ClassDescriptor) classifier;
217                return clazz.getKind().isSingleton();
218            }
219            return false;
220        }
221    
222        public static boolean isEnumClass(@NotNull DeclarationDescriptor descriptor) {
223            return isKindOf(descriptor, ClassKind.ENUM_CLASS);
224        }
225    
226        public static boolean isAnnotationClass(@Nullable DeclarationDescriptor descriptor) {
227            return isKindOf(descriptor, ClassKind.ANNOTATION_CLASS);
228        }
229    
230        public static boolean isTrait(@Nullable DeclarationDescriptor descriptor) {
231            return isKindOf(descriptor, ClassKind.TRAIT);
232        }
233    
234        public static boolean isClass(@Nullable DeclarationDescriptor descriptor) {
235            return isKindOf(descriptor, ClassKind.CLASS);
236        }
237    
238        public static boolean isKindOf(@Nullable DeclarationDescriptor descriptor, @NotNull ClassKind classKind) {
239            return descriptor instanceof ClassDescriptor && ((ClassDescriptor) descriptor).getKind() == classKind;
240        }
241    
242        @NotNull
243        public static List<ClassDescriptor> getSuperclassDescriptors(@NotNull ClassDescriptor classDescriptor) {
244            Collection<JetType> superclassTypes = classDescriptor.getTypeConstructor().getSupertypes();
245            List<ClassDescriptor> superClassDescriptors = new ArrayList<ClassDescriptor>();
246            for (JetType type : superclassTypes) {
247                ClassDescriptor result = getClassDescriptorForType(type);
248                if (!isAny(result)) {
249                    superClassDescriptors.add(result);
250                }
251            }
252            return superClassDescriptors;
253        }
254    
255        @NotNull
256        public static ClassDescriptor getClassDescriptorForType(@NotNull JetType type) {
257            return getClassDescriptorForTypeConstructor(type.getConstructor());
258        }
259    
260        @NotNull
261        public static ClassDescriptor getClassDescriptorForTypeConstructor(@NotNull TypeConstructor typeConstructor) {
262            ClassifierDescriptor descriptor = typeConstructor.getDeclarationDescriptor();
263            assert descriptor instanceof ClassDescriptor
264                : "Classifier descriptor of a type should be of type ClassDescriptor: " + typeConstructor;
265            return (ClassDescriptor) descriptor;
266        }
267    
268        public static boolean isAny(@NotNull DeclarationDescriptor superClassDescriptor) {
269            return superClassDescriptor.equals(KotlinBuiltIns.getInstance().getAny());
270        }
271    
272        public static boolean isEnumClassObject(@NotNull DeclarationDescriptor descriptor) {
273            if (descriptor instanceof ClassDescriptor && ((ClassDescriptor) descriptor).getKind() == ClassKind.CLASS_OBJECT) {
274                DeclarationDescriptor containing = descriptor.getContainingDeclaration();
275                if ((containing instanceof ClassDescriptor) && ((ClassDescriptor) containing).getKind() == ClassKind.ENUM_CLASS) {
276                    return true;
277                }
278            }
279            return false;
280        }
281    
282        public static boolean isSyntheticClassObject(@NotNull DeclarationDescriptor descriptor) {
283            if (isClassObject(descriptor)) {
284                DeclarationDescriptor containing = descriptor.getContainingDeclaration();
285                if (containing != null) {
286                    return isEnumClass(containing) || isObject(containing) || isEnumEntry(containing);
287                }
288            }
289            return false;
290        }
291    
292        @NotNull
293        public static Visibility getDefaultConstructorVisibility(@NotNull ClassDescriptor classDescriptor) {
294            ClassKind classKind = classDescriptor.getKind();
295            if (classKind == ClassKind.ENUM_CLASS || classKind.isSingleton() || isAnonymousObject(classDescriptor)) {
296                return Visibilities.PRIVATE;
297            }
298            assert classKind == ClassKind.CLASS || classKind == ClassKind.TRAIT || classKind == ClassKind.ANNOTATION_CLASS;
299            return Visibilities.PUBLIC;
300        }
301    
302        @NotNull
303        public static Visibility getSyntheticClassObjectVisibility() {
304            return Visibilities.PUBLIC;
305        }
306    
307        @Nullable
308        public static ClassDescriptor getInnerClassByName(@NotNull ClassDescriptor classDescriptor, @NotNull String innerClassName) {
309            ClassifierDescriptor classifier = classDescriptor.getDefaultType().getMemberScope().getClassifier(Name.identifier(innerClassName));
310            assert classifier instanceof ClassDescriptor :
311                    "Inner class " + innerClassName + " in " + classDescriptor + " should be instance of ClassDescriptor, but was: "
312                    + (classifier == null ? "null" : classifier.getClass());
313            return (ClassDescriptor) classifier;
314        }
315    
316        @Nullable
317        public static JetType getReceiverParameterType(@Nullable ReceiverParameterDescriptor receiverParameterDescriptor) {
318            return receiverParameterDescriptor == null ? null : receiverParameterDescriptor.getType();
319        }
320    
321        public static boolean isConstructorOfStaticNestedClass(@Nullable CallableDescriptor descriptor) {
322            return descriptor instanceof ConstructorDescriptor && isStaticNestedClass(descriptor.getContainingDeclaration());
323        }
324    
325        /**
326         * @return true if descriptor is a class inside another class and does not have access to the outer class
327         */
328        public static boolean isStaticNestedClass(@NotNull DeclarationDescriptor descriptor) {
329            DeclarationDescriptor containing = descriptor.getContainingDeclaration();
330            return descriptor instanceof ClassDescriptor &&
331                   containing instanceof ClassDescriptor &&
332                   !((ClassDescriptor) descriptor).isInner();
333        }
334    
335        @NotNull
336        public static JetScope getStaticNestedClassesScope(@NotNull ClassDescriptor descriptor) {
337            JetScope innerClassesScope = descriptor.getUnsubstitutedInnerClassesScope();
338            return new FilteringScope(innerClassesScope, new Predicate<DeclarationDescriptor>() {
339                @Override
340                public boolean apply(@Nullable DeclarationDescriptor descriptor) {
341                    return descriptor instanceof ClassDescriptor && !((ClassDescriptor) descriptor).isInner();
342                }
343            });
344        }
345    
346        public static boolean isEnumValueOfMethod(@NotNull FunctionDescriptor functionDescriptor) {
347            List<ValueParameterDescriptor> methodTypeParameters = functionDescriptor.getValueParameters();
348            JetType nullableString = TypeUtils.makeNullable(KotlinBuiltIns.getInstance().getStringType());
349            return "valueOf".equals(functionDescriptor.getName().asString())
350                   && methodTypeParameters.size() == 1
351                   && JetTypeChecker.DEFAULT.isSubtypeOf(methodTypeParameters.get(0).getType(), nullableString);
352        }
353    
354        public static boolean isEnumValuesMethod(@NotNull FunctionDescriptor functionDescriptor) {
355            List<ValueParameterDescriptor> methodTypeParameters = functionDescriptor.getValueParameters();
356            return "values".equals(functionDescriptor.getName().asString())
357                   && methodTypeParameters.isEmpty();
358        }
359    
360        /**
361         * @return true iff {@code descriptor}'s first non-class container is a package
362         */
363        public static boolean isTopLevelOrInnerClass(@NotNull ClassDescriptor descriptor) {
364            DeclarationDescriptor containing = descriptor.getContainingDeclaration();
365            return isTopLevelDeclaration(descriptor) ||
366                   containing instanceof ClassDescriptor && isTopLevelOrInnerClass((ClassDescriptor) containing);
367        }
368    
369        /**
370         * Given a fake override, finds any declaration of it in the overridden descriptors. Keep in mind that there may be many declarations
371         * of the fake override in the supertypes, this method finds just the only one.
372         * TODO: probably all call-sites of this method are wrong, they should handle all super-declarations
373         */
374        @NotNull
375        public static <D extends CallableMemberDescriptor> D unwrapFakeOverride(@NotNull D descriptor) {
376            while (descriptor.getKind() == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) {
377                Set<? extends CallableMemberDescriptor> overridden = descriptor.getOverriddenDescriptors();
378                if (overridden.isEmpty()) {
379                    throw new IllegalStateException("Fake override should have at least one overridden descriptor: " + descriptor);
380                }
381                //noinspection unchecked
382                descriptor = (D) overridden.iterator().next();
383            }
384            return descriptor;
385        }
386    
387        public static boolean shouldRecordInitializerForProperty(@NotNull VariableDescriptor variable, @NotNull JetType type) {
388            if (variable.isVar() || type.isError()) return false;
389    
390            if (type instanceof LazyType || type.isNullable()) return true;
391    
392            KotlinBuiltIns builtIns = KotlinBuiltIns.getInstance();
393            return builtIns.isPrimitiveType(type) ||
394                   builtIns.getStringType().equals(type) ||
395                   builtIns.getNumber().getDefaultType().equals(type) ||
396                   builtIns.getAnyType().equals(type);
397        }
398    }