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.intellij.psi.PsiElement;
020    import com.intellij.psi.PsiNameIdentifierOwner;
021    import org.jetbrains.annotations.NotNull;
022    import org.jetbrains.annotations.Nullable;
023    import org.jetbrains.jet.lang.descriptors.*;
024    import org.jetbrains.jet.lang.descriptors.impl.*;
025    import org.jetbrains.jet.lang.psi.*;
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.ChainedScope;
029    import org.jetbrains.jet.lang.resolve.scopes.JetScope;
030    import org.jetbrains.jet.lang.resolve.scopes.WritableScope;
031    import org.jetbrains.jet.lang.resolve.scopes.WriteThroughScope;
032    import org.jetbrains.jet.lang.types.JetType;
033    import org.jetbrains.jet.storage.LockBasedStorageManager;
034    import org.jetbrains.jet.utils.DFS;
035    
036    import javax.inject.Inject;
037    import java.util.*;
038    
039    import static org.jetbrains.jet.lang.diagnostics.Errors.*;
040    import static org.jetbrains.jet.lang.resolve.BindingContext.FQNAME_TO_CLASS_DESCRIPTOR;
041    import static org.jetbrains.jet.lang.resolve.BindingContext.TYPE;
042    import static org.jetbrains.jet.lang.resolve.DescriptorUtils.isEnumEntry;
043    import static org.jetbrains.jet.lang.resolve.DescriptorUtils.isObject;
044    import static org.jetbrains.jet.lang.resolve.ModifiersChecker.getDefaultClassVisibility;
045    import static org.jetbrains.jet.lang.resolve.ModifiersChecker.resolveVisibilityFromModifiers;
046    import static org.jetbrains.jet.lang.resolve.name.SpecialNames.getClassObjectName;
047    import static org.jetbrains.jet.lang.resolve.source.SourcePackage.toSourceElement;
048    
049    public class TypeHierarchyResolver {
050        private static final DFS.Neighbors<ClassDescriptor> CLASS_INHERITANCE_EDGES = new DFS.Neighbors<ClassDescriptor>() {
051            @NotNull
052            @Override
053            public Iterable<ClassDescriptor> getNeighbors(ClassDescriptor current) {
054                List<ClassDescriptor> result = new ArrayList<ClassDescriptor>();
055                for (JetType supertype : current.getDefaultType().getConstructor().getSupertypes()) {
056                    DeclarationDescriptor descriptor = supertype.getConstructor().getDeclarationDescriptor();
057                    if (descriptor instanceof ClassDescriptor) {
058                        result.add((ClassDescriptor) descriptor);
059                    }
060                }
061                DeclarationDescriptor container = current.getContainingDeclaration();
062                if (container instanceof ClassDescriptor) {
063                    result.add((ClassDescriptor) container);
064                }
065                return result;
066            }
067        };
068    
069        @NotNull
070        private ImportsResolver importsResolver;
071        @NotNull
072        private DescriptorResolver descriptorResolver;
073        @NotNull
074        private ScriptHeaderResolver scriptHeaderResolver;
075        @NotNull
076        private MutablePackageFragmentProvider packageFragmentProvider;
077        @NotNull
078        private BindingTrace trace;
079    
080        @Inject
081        public void setImportsResolver(@NotNull ImportsResolver importsResolver) {
082            this.importsResolver = importsResolver;
083        }
084    
085        @Inject
086        public void setDescriptorResolver(@NotNull DescriptorResolver descriptorResolver) {
087            this.descriptorResolver = descriptorResolver;
088        }
089    
090        // SCRIPT: inject script header resolver
091        @Inject
092        public void setScriptHeaderResolver(@NotNull ScriptHeaderResolver scriptHeaderResolver) {
093            this.scriptHeaderResolver = scriptHeaderResolver;
094        }
095    
096        @Inject
097        public void setPackageFragmentProvider(@NotNull MutablePackageFragmentProvider packageFragmentProvider) {
098            this.packageFragmentProvider = packageFragmentProvider;
099        }
100    
101        @Inject
102        public void setTrace(@NotNull BindingTrace trace) {
103            this.trace = trace;
104        }
105    
106        public void process(
107                @NotNull TopDownAnalysisContext c,
108                @NotNull JetScope outerScope,
109                @NotNull PackageLikeBuilder owner,
110                @NotNull Collection<? extends PsiElement> declarations
111        ) {
112    
113            {
114                // TODO: Very temp code - main goal is to remove recursion from collectPackageFragmentsAndClassifiers
115                Queue<JetDeclarationContainer> forDeferredResolve = new LinkedList<JetDeclarationContainer>();
116                forDeferredResolve.addAll(collectPackageFragmentsAndClassifiers(c, outerScope, owner, declarations));
117    
118                while (!forDeferredResolve.isEmpty()) {
119                    JetDeclarationContainer declarationContainer = forDeferredResolve.poll();
120                    assert declarationContainer != null;
121    
122                    DeclarationDescriptor descriptorForDeferredResolve = c.forDeferredResolver.get(declarationContainer);
123                    JetScope scope = c.normalScope.get(declarationContainer);
124    
125                    // Even more temp code
126                    if (descriptorForDeferredResolve instanceof MutableClassDescriptor) {
127                        forDeferredResolve.addAll(
128                                collectPackageFragmentsAndClassifiers(
129                                        c,
130                                        scope,
131                                        ((MutableClassDescriptor) descriptorForDeferredResolve).getBuilder(),
132                                        declarationContainer.getDeclarations()));
133                    }
134                    else if (descriptorForDeferredResolve instanceof MutablePackageFragmentDescriptor) {
135                        forDeferredResolve.addAll(
136                                collectPackageFragmentsAndClassifiers(
137                                        c,
138                                        scope,
139                                        ((MutablePackageFragmentDescriptor) descriptorForDeferredResolve).getBuilder(),
140                                        declarationContainer.getDeclarations()));
141                    }
142                    else {
143                        assert false;
144                    }
145                }
146            }
147    
148            importsResolver.processTypeImports(c);
149    
150            createTypeConstructors(c); // create type constructors for classes and generic parameters, supertypes are not filled in
151            resolveTypesInClassHeaders(c); // Generic bounds and types in supertype lists (no expressions or constructor resolution)
152    
153            c.setClassesTopologicalOrder(topologicallySortClassesAndObjects(c));
154    
155            // Detect and disconnect all loops in the hierarchy
156            detectAndDisconnectLoops(c);
157        }
158    
159        @NotNull
160        private Collection<JetDeclarationContainer> collectPackageFragmentsAndClassifiers(
161                @NotNull TopDownAnalysisContext c,
162                @NotNull JetScope outerScope,
163                @NotNull PackageLikeBuilder owner,
164                @NotNull Iterable<? extends PsiElement> declarations
165        ) {
166            Collection<JetDeclarationContainer> forDeferredResolve = new ArrayList<JetDeclarationContainer>();
167    
168            ClassifierCollector collector = new ClassifierCollector(c, outerScope, owner, forDeferredResolve);
169    
170            for (PsiElement declaration : declarations) {
171                declaration.accept(collector);
172            }
173    
174            return forDeferredResolve;
175        }
176    
177    
178        @NotNull
179        private static ClassKind getClassKind(@NotNull JetClass jetClass) {
180            if (jetClass.isTrait()) return ClassKind.TRAIT;
181            if (jetClass.isAnnotation()) return ClassKind.ANNOTATION_CLASS;
182            if (jetClass.isEnum()) return ClassKind.ENUM_CLASS;
183            return ClassKind.CLASS;
184        }
185    
186        private void createTypeConstructors(@NotNull TopDownAnalysisContext c) {
187            for (Map.Entry<JetClassOrObject, ClassDescriptorWithResolutionScopes> entry : c.getDeclaredClasses().entrySet()) {
188                JetClassOrObject classOrObject = entry.getKey();
189                MutableClassDescriptor descriptor = (MutableClassDescriptor) entry.getValue();
190                if (classOrObject instanceof JetClass) {
191                    descriptorResolver.resolveMutableClassDescriptor(
192                            c.getTopDownAnalysisParameters(),
193                            (JetClass) classOrObject, descriptor, trace);
194                }
195                else if (classOrObject instanceof JetObjectDeclaration) {
196                    descriptor.setModality(Modality.FINAL);
197                    descriptor.setVisibility(resolveVisibilityFromModifiers(classOrObject, getDefaultClassVisibility(descriptor)));
198                    descriptor.setTypeParameterDescriptors(Collections.<TypeParameterDescriptor>emptyList());
199                }
200    
201                descriptor.createTypeConstructor();
202    
203                ClassKind kind = descriptor.getKind();
204                if (kind == ClassKind.ENUM_ENTRY || kind == ClassKind.OBJECT) {
205                    MutableClassDescriptor classObject = (MutableClassDescriptor) descriptor.getClassObjectDescriptor();
206                    assert classObject != null : "Enum entries and named objects should have class objects: " + classOrObject.getText();
207    
208                    // This is a clever hack: each enum entry and object declaration (i.e. singleton) has a synthetic class object.
209                    // We make this class object inherit from the singleton here, thus allowing to use the singleton's class object where
210                    // the instance of the singleton is applicable. Effectively all members of the singleton would be present in its class
211                    // object as fake overrides, so you can access them via standard class object notation: ObjectName.memberName()
212                    classObject.setSupertypes(Collections.singleton(descriptor.getDefaultType()));
213    
214                    classObject.createTypeConstructor();
215                }
216            }
217        }
218    
219        private void resolveTypesInClassHeaders(@NotNull TopDownAnalysisContext c) {
220            for (Map.Entry<JetClassOrObject, ClassDescriptorWithResolutionScopes> entry : c.getDeclaredClasses().entrySet()) {
221                JetClassOrObject classOrObject = entry.getKey();
222                if (classOrObject instanceof JetClass) {
223                    ClassDescriptorWithResolutionScopes descriptor = entry.getValue();
224                    //noinspection unchecked
225                    descriptorResolver.resolveGenericBounds((JetClass) classOrObject, descriptor, descriptor.getScopeForClassHeaderResolution(),
226                                                            (List) descriptor.getTypeConstructor().getParameters(), trace);
227                }
228            }
229    
230            for (Map.Entry<JetClassOrObject, ClassDescriptorWithResolutionScopes> entry : c.getDeclaredClasses().entrySet()) {
231                descriptorResolver.resolveSupertypesForMutableClassDescriptor(entry.getKey(), (MutableClassDescriptor) entry.getValue(), trace);
232            }
233        }
234    
235        @NotNull
236        @SuppressWarnings("unchecked")
237        private static List<MutableClassDescriptor> topologicallySortClassesAndObjects(@NotNull TopDownAnalysisContext c) {
238            Collection<ClassDescriptor> sourceClasses = (Collection) c.getAllClasses();
239            List<ClassDescriptor> allClassesOrdered = DFS.topologicalOrder(sourceClasses, CLASS_INHERITANCE_EDGES);
240            allClassesOrdered.retainAll(sourceClasses);
241            return (List) allClassesOrdered;
242        }
243    
244        private void detectAndDisconnectLoops(@NotNull TopDownAnalysisContext c) {
245            List<Runnable> tasks = new ArrayList<Runnable>();
246            for (final MutableClassDescriptor klass : c.getClassesTopologicalOrder()) {
247                for (final JetType supertype : klass.getSupertypes()) {
248                    ClassifierDescriptor supertypeDescriptor = supertype.getConstructor().getDeclarationDescriptor();
249                    if (supertypeDescriptor instanceof ClassDescriptor) {
250                        ClassDescriptor superclass = (ClassDescriptor) supertypeDescriptor;
251                        if (isReachable(superclass, klass, new HashSet<ClassDescriptor>())) {
252                            tasks.add(new Runnable() {
253                                @Override
254                                public void run() {
255                                    klass.getSupertypes().remove(supertype);
256                                }
257                            });
258                            reportCyclicInheritanceHierarchyError(trace, klass, superclass);
259                        }
260                    }
261                }
262            }
263    
264            for (Runnable task : tasks) {
265                task.run();
266            }
267        }
268    
269        // TODO: use DFS and copy to LazyClassTypeConstructor.isReachable
270        private static boolean isReachable(
271                @NotNull ClassDescriptor from,
272                @NotNull MutableClassDescriptor to,
273                @NotNull Set<ClassDescriptor> visited
274        ) {
275            if (!visited.add(from)) return false;
276            for (ClassDescriptor superclass : CLASS_INHERITANCE_EDGES.getNeighbors(from)) {
277                if (superclass == to || isReachable(superclass, to, visited)) return true;
278            }
279            return false;
280        }
281    
282        public static void reportCyclicInheritanceHierarchyError(
283                @NotNull BindingTrace trace,
284                @NotNull ClassDescriptor classDescriptor,
285                @NotNull ClassDescriptor superclass
286        ) {
287            PsiElement psiElement = DescriptorToSourceUtils.classDescriptorToDeclaration(classDescriptor);
288    
289            PsiElement elementToMark = null;
290            if (psiElement instanceof JetClassOrObject) {
291                JetClassOrObject classOrObject = (JetClassOrObject) psiElement;
292                for (JetDelegationSpecifier delegationSpecifier : classOrObject.getDelegationSpecifiers()) {
293                    JetTypeReference typeReference = delegationSpecifier.getTypeReference();
294                    if (typeReference == null) continue;
295                    JetType supertype = trace.get(TYPE, typeReference);
296                    if (supertype != null && supertype.getConstructor() == superclass.getTypeConstructor()) {
297                        elementToMark = typeReference;
298                    }
299                }
300            }
301            if (elementToMark == null && psiElement instanceof PsiNameIdentifierOwner) {
302                PsiNameIdentifierOwner namedElement = (PsiNameIdentifierOwner) psiElement;
303                PsiElement nameIdentifier = namedElement.getNameIdentifier();
304                if (nameIdentifier != null) {
305                    elementToMark = nameIdentifier;
306                }
307            }
308            if (elementToMark != null) {
309                trace.report(CYCLIC_INHERITANCE_HIERARCHY.on(elementToMark));
310            }
311        }
312    
313        private class ClassifierCollector extends JetVisitorVoid {
314            private final TopDownAnalysisContext c;
315            private final JetScope outerScope;
316            private final PackageLikeBuilder owner;
317            private final Collection<JetDeclarationContainer> forDeferredResolve;
318    
319            public ClassifierCollector(
320                    @NotNull TopDownAnalysisContext c,
321                    @NotNull JetScope outerScope,
322                    @NotNull PackageLikeBuilder owner,
323                    @NotNull Collection<JetDeclarationContainer> forDeferredResolve
324            ) {
325                this.c = c;
326                this.outerScope = outerScope;
327                this.owner = owner;
328                this.forDeferredResolve = forDeferredResolve;
329            }
330    
331            @Override
332            public void visitJetFile(@NotNull JetFile file) {
333                MutablePackageFragmentDescriptor packageFragment = getOrCreatePackageFragmentForFile(file);
334                c.getPackageFragments().put(file, packageFragment);
335                c.addFile(file);
336    
337                PackageViewDescriptor packageView = packageFragment.getContainingDeclaration().getPackage(packageFragment.getFqName());
338                ChainedScope rootPlusPackageScope = new ChainedScope(packageView, "Root scope for " + file, packageView.getMemberScope(), outerScope);
339                WriteThroughScope packageScope = new WriteThroughScope(rootPlusPackageScope, packageFragment.getMemberScope(),
340                                                                         new TraceBasedRedeclarationHandler(trace), "package in file " + file.getName());
341                packageScope.changeLockLevel(WritableScope.LockLevel.BOTH);
342                c.getFileScopes().put(file, packageScope);
343    
344                if (file.isScript()) {
345                    // SCRIPT: process script hierarchy
346                    scriptHeaderResolver.processScriptHierarchy(c, file.getScript(), packageScope);
347                }
348    
349                prepareForDeferredCall(packageScope, packageFragment, file);
350            }
351    
352            @Override
353            public void visitClass(@NotNull JetClass klass) {
354                MutableClassDescriptor mutableClassDescriptor = createClassDescriptorForClass(klass, owner.getOwnerForChildren());
355    
356                owner.addClassifierDescriptor(mutableClassDescriptor);
357            }
358    
359            @Override
360            public void visitObjectDeclaration(@NotNull JetObjectDeclaration declaration) {
361                if (declaration.isObjectLiteral()) {
362                    createClassDescriptorForSingleton(declaration, SpecialNames.NO_NAME_PROVIDED, ClassKind.CLASS);
363                    return;
364                }
365    
366                MutableClassDescriptor descriptor =
367                        createClassDescriptorForSingleton(declaration, JetPsiUtil.safeName(declaration.getName()), ClassKind.OBJECT);
368    
369                owner.addClassifierDescriptor(descriptor);
370                trace.record(FQNAME_TO_CLASS_DESCRIPTOR, JetNamedDeclarationUtil.getUnsafeFQName(declaration), descriptor);
371    
372                descriptor.getBuilder().setClassObjectDescriptor(createSyntheticClassObjectForSingleton(descriptor));
373            }
374    
375            @Override
376            public void visitEnumEntry(@NotNull JetEnumEntry declaration) {
377                MutableClassDescriptor descriptor =
378                        createClassDescriptorForSingleton(declaration, JetPsiUtil.safeName(declaration.getName()), ClassKind.ENUM_ENTRY);
379    
380                owner.addClassifierDescriptor(descriptor);
381    
382                descriptor.getBuilder().setClassObjectDescriptor(createSyntheticClassObjectForSingleton(descriptor));
383            }
384    
385            @Override
386            public void visitTypedef(@NotNull JetTypedef typedef) {
387                trace.report(UNSUPPORTED.on(typedef, "TypeHierarchyResolver"));
388            }
389    
390            @Override
391            public void visitClassObject(@NotNull JetClassObject classObject) {
392                JetObjectDeclaration objectDeclaration = classObject.getObjectDeclaration();
393    
394                DeclarationDescriptor container = owner.getOwnerForChildren();
395    
396                MutableClassDescriptor classObjectDescriptor =
397                        createClassDescriptorForSingleton(objectDeclaration, getClassObjectName(container.getName()), ClassKind.CLASS_OBJECT);
398    
399                PackageLikeBuilder.ClassObjectStatus status =
400                        isEnumEntry(container) || isObject(container) || c.getTopDownAnalysisParameters().isDeclaredLocally() ?
401                        PackageLikeBuilder.ClassObjectStatus.NOT_ALLOWED :
402                        owner.setClassObjectDescriptor(classObjectDescriptor);
403    
404                switch (status) {
405                    case DUPLICATE:
406                        trace.report(MANY_CLASS_OBJECTS.on(classObject));
407                        break;
408                    case NOT_ALLOWED:
409                        trace.report(CLASS_OBJECT_NOT_ALLOWED.on(classObject));
410                        break;
411                    case OK:
412                        // Everything is OK so no errors to trace.
413                        break;
414                }
415            }
416    
417            @NotNull
418            private MutablePackageFragmentDescriptor getOrCreatePackageFragmentForFile(@NotNull JetFile file) {
419                JetPackageDirective packageDirective = file.getPackageDirective();
420                assert packageDirective != null : "scripts are not supported";
421    
422                MutablePackageFragmentDescriptor fragment = packageFragmentProvider.getOrCreateFragment(packageDirective.getFqName());
423    
424                ModuleDescriptor module = packageFragmentProvider.getModule();
425                DescriptorResolver.resolvePackageHeader(packageDirective, module, trace);
426                DescriptorResolver.registerFileInPackage(trace, file);
427                trace.record(BindingContext.FILE_TO_PACKAGE_FRAGMENT, file, fragment);
428    
429                return fragment;
430            }
431    
432    
433            @NotNull
434            private MutableClassDescriptor createSyntheticClassObjectForSingleton(@NotNull ClassDescriptor classDescriptor) {
435                MutableClassDescriptor classObject =
436                        new MutableClassDescriptor(classDescriptor, outerScope, ClassKind.CLASS_OBJECT, false,
437                                                   getClassObjectName(classDescriptor.getName()), SourceElement.NO_SOURCE);
438    
439                classObject.setModality(Modality.FINAL);
440                classObject.setVisibility(DescriptorUtils.getSyntheticClassObjectVisibility());
441                classObject.setTypeParameterDescriptors(Collections.<TypeParameterDescriptor>emptyList());
442                createPrimaryConstructorForObject(null, classObject);
443                return classObject;
444            }
445    
446            @NotNull
447            private MutableClassDescriptor createClassDescriptorForClass(
448                    @NotNull JetClass klass,
449                    @NotNull DeclarationDescriptor containingDeclaration
450            ) {
451                ClassKind kind = getClassKind(klass);
452                // Kind check is needed in order to not consider enums as inner in any case
453                // (otherwise it would be impossible to create a class object in the enum)
454                boolean isInner = kind == ClassKind.CLASS && klass.isInner();
455                MutableClassDescriptor descriptor = new MutableClassDescriptor(
456                        containingDeclaration, outerScope, kind, isInner, JetPsiUtil.safeName(klass.getName()),
457                        toSourceElement(klass));
458                c.getDeclaredClasses().put(klass, descriptor);
459                trace.record(FQNAME_TO_CLASS_DESCRIPTOR, JetNamedDeclarationUtil.getUnsafeFQName(klass), descriptor);
460    
461                if (descriptor.getKind() == ClassKind.ENUM_CLASS) {
462                    ClassDescriptor classObject = new EnumClassObjectDescriptor(LockBasedStorageManager.NO_LOCKS, descriptor);
463                    descriptor.getBuilder().setClassObjectDescriptor(classObject);
464                }
465    
466                prepareForDeferredCall(descriptor.getScopeForMemberDeclarationResolution(), descriptor, klass);
467    
468                return descriptor;
469            }
470    
471            @NotNull
472            private MutableClassDescriptor createClassDescriptorForSingleton(
473                    @NotNull JetClassOrObject declaration,
474                    @NotNull Name name,
475                    @NotNull ClassKind kind
476            ) {
477                MutableClassDescriptor descriptor = new MutableClassDescriptor(owner.getOwnerForChildren(), outerScope, kind, false, name,
478                                                                               toSourceElement(declaration));
479    
480                prepareForDeferredCall(descriptor.getScopeForMemberDeclarationResolution(), descriptor, declaration);
481    
482                createPrimaryConstructorForObject(declaration, descriptor);
483                trace.record(BindingContext.CLASS, declaration, descriptor);
484    
485                c.getDeclaredClasses().put(declaration, descriptor);
486    
487                return descriptor;
488            }
489    
490            @NotNull
491            private ConstructorDescriptorImpl createPrimaryConstructorForObject(
492                    @Nullable JetClassOrObject object,
493                    @NotNull MutableClassDescriptor mutableClassDescriptor
494            ) {
495                ConstructorDescriptorImpl constructorDescriptor = DescriptorResolver
496                        .createAndRecordPrimaryConstructorForObject(object, mutableClassDescriptor, trace);
497                mutableClassDescriptor.setPrimaryConstructor(constructorDescriptor);
498                return constructorDescriptor;
499            }
500    
501            private void prepareForDeferredCall(
502                    @NotNull JetScope outerScope,
503                    @NotNull DeclarationDescriptor descriptorForDeferredResolve,
504                    @NotNull JetDeclarationContainer container
505            ) {
506                forDeferredResolve.add(container);
507                c.normalScope.put(container, outerScope);
508                c.forDeferredResolver.put(container, descriptorForDeferredResolve);
509            }
510        }
511    }