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