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