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.renderer;
018    
019    import com.google.common.collect.Lists;
020    import com.google.common.collect.Sets;
021    import com.intellij.openapi.util.text.StringUtil;
022    import org.jetbrains.annotations.NotNull;
023    import org.jetbrains.jet.lang.descriptors.*;
024    import org.jetbrains.jet.lang.descriptors.annotations.Annotated;
025    import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
026    import org.jetbrains.jet.lang.descriptors.impl.DeclarationDescriptorVisitorEmptyBodies;
027    import org.jetbrains.jet.lang.resolve.DescriptorUtils;
028    import org.jetbrains.jet.lang.resolve.name.FqName;
029    import org.jetbrains.jet.lang.resolve.name.FqNameUnsafe;
030    import org.jetbrains.jet.lang.resolve.name.Name;
031    import org.jetbrains.jet.lang.types.*;
032    import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
033    
034    import java.util.*;
035    
036    import static org.jetbrains.jet.lang.types.TypeUtils.CANT_INFER_LAMBDA_PARAM_TYPE;
037    import static org.jetbrains.jet.lang.types.TypeUtils.CANT_INFER_TYPE_PARAMETER;
038    
039    public class DescriptorRendererImpl implements DescriptorRenderer {
040        private final boolean shortNames;
041        private final boolean withDefinedIn;
042        private final Set<DescriptorRenderer.Modifier> modifiers;
043        private final boolean startFromName;
044        private final boolean debugMode;
045        private final boolean classWithPrimaryConstructor;
046        private final boolean verbose;
047        private final boolean unitReturnType;
048        private final boolean normalizedVisibilities;
049        private final boolean showInternalKeyword;
050        private final boolean alwaysRenderAny;
051        private final boolean prettyFunctionTypes;
052        @NotNull
053        private final OverrideRenderingPolicy overrideRenderingPolicy;
054        @NotNull
055        private final ValueParametersHandler handler;
056        @NotNull
057        private final TextFormat textFormat;
058        @NotNull
059        private final Set<FqName> excludedAnnotationClasses;
060    
061        /* package */ DescriptorRendererImpl(
062                boolean shortNames,
063                boolean withDefinedIn,
064                Set<DescriptorRenderer.Modifier> modifiers,
065                boolean startFromName,
066                boolean debugMode,
067                boolean classWithPrimaryConstructor,
068                boolean verbose,
069                boolean unitReturnType,
070                boolean normalizedVisibilities,
071                boolean showInternalKeyword,
072                boolean alwaysRenderAny,
073                boolean prettyFunctionTypes,
074                @NotNull OverrideRenderingPolicy overrideRenderingPolicy,
075                @NotNull ValueParametersHandler handler,
076                @NotNull TextFormat textFormat,
077                @NotNull Collection<FqName> excludedAnnotationClasses
078        ) {
079            this.shortNames = shortNames;
080            this.withDefinedIn = withDefinedIn;
081            this.modifiers = modifiers;
082            this.startFromName = startFromName;
083            this.handler = handler;
084            this.classWithPrimaryConstructor = classWithPrimaryConstructor;
085            this.verbose = verbose;
086            this.unitReturnType = unitReturnType;
087            this.normalizedVisibilities = normalizedVisibilities;
088            this.showInternalKeyword = showInternalKeyword;
089            this.overrideRenderingPolicy = overrideRenderingPolicy;
090            this.debugMode = debugMode;
091            this.textFormat = textFormat;
092            this.excludedAnnotationClasses = Sets.newHashSet(excludedAnnotationClasses);
093            this.alwaysRenderAny = alwaysRenderAny;
094            this.prettyFunctionTypes = prettyFunctionTypes;
095        }
096    
097    
098        /* FORMATTING */
099        @NotNull
100        private String renderKeyword(@NotNull String keyword) {
101            switch (textFormat) {
102                case PLAIN:
103                    return keyword;
104                case HTML:
105                    return "<b>" + keyword + "</b>";
106            }
107            throw new IllegalStateException("Unexpected textFormat: " + textFormat);
108        }
109    
110        @NotNull
111        private String escape(@NotNull String string) {
112            switch (textFormat) {
113                case PLAIN:
114                    return string;
115                case HTML:
116                    return string.replaceAll("<", "&lt;").replaceAll(">", "&gt;");
117            }
118            throw new IllegalStateException("Unexpected textFormat: " + textFormat);
119        }
120    
121        @NotNull
122        private String lt() {
123            return escape("<");
124        }
125    
126        @NotNull
127        private String arrow() {
128            switch (textFormat) {
129                case PLAIN:
130                    return escape("->");
131                case HTML:
132                    return "&rarr;";
133            }
134            throw new IllegalStateException("Unexpected textFormat: " + textFormat);
135        }
136    
137        @NotNull
138        private String renderMessage(@NotNull String message) {
139            switch (textFormat) {
140                case PLAIN:
141                    return message;
142                case HTML:
143                    return "<i>" + message + "</i>";
144            }
145            throw new IllegalStateException("Unexpected textFormat: " + textFormat);
146        }
147    
148    
149        /* NAMES RENDERING */
150        @NotNull
151        private String renderName(@NotNull Name identifier) {
152            String asString = identifier.toString();
153            return escape(KeywordStringsGenerated.KEYWORDS.contains(asString) ? '`' + asString + '`' : asString);
154        }
155    
156        private void renderName(@NotNull DeclarationDescriptor descriptor, @NotNull StringBuilder builder) {
157            builder.append(renderName(descriptor.getName()));
158        }
159    
160        @NotNull
161        private String renderFqName(@NotNull FqNameUnsafe fqName) {
162            return renderFqName(fqName.pathSegments());
163        }
164    
165    
166        @NotNull
167        private String renderFqName(@NotNull List<Name> pathSegments) {
168            StringBuilder buf = new StringBuilder();
169            for (Name element : pathSegments) {
170                if (buf.length() != 0) {
171                    buf.append(".");
172                }
173                buf.append(renderName(element));
174            }
175            return buf.toString();
176        }
177    
178        @NotNull
179        private String renderClassName(@NotNull ClassDescriptor klass) {
180            if (ErrorUtils.isError(klass)) {
181                return klass.getTypeConstructor().toString();
182            }
183            if (shortNames) {
184                List<Name> qualifiedNameElements = Lists.newArrayList();
185    
186                // for nested classes qualified name should be used
187                DeclarationDescriptor current = klass;
188                do {
189                    if (((ClassDescriptor) current).getKind() != ClassKind.CLASS_OBJECT) {
190                        qualifiedNameElements.add(current.getName());
191                    }
192                    current = current.getContainingDeclaration();
193                }
194                while (current instanceof ClassDescriptor);
195    
196                Collections.reverse(qualifiedNameElements);
197                return renderFqName(qualifiedNameElements);
198            }
199            return renderFqName(DescriptorUtils.getFQName(klass));
200        }
201    
202        /* TYPES RENDERING */
203        @NotNull
204        @Override
205        public String renderType(@NotNull JetType type) {
206            return escape(renderTypeWithoutEscape(type));
207        }
208    
209        private String renderTypeWithoutEscape(@NotNull JetType type) {
210            if (type == CANT_INFER_LAMBDA_PARAM_TYPE || type == CANT_INFER_TYPE_PARAMETER) {
211                return "???";
212            }
213            if (type.isError()) {
214                return type.toString();
215            }
216            if (KotlinBuiltIns.getInstance().isFunctionOrExtensionFunctionType(type) && prettyFunctionTypes) {
217                return renderFunctionType(type);
218            }
219            return renderDefaultType(type);
220        }
221    
222        @NotNull
223        private String renderDefaultType(@NotNull JetType type) {
224            StringBuilder sb = new StringBuilder();
225    
226            sb.append(renderTypeName(type.getConstructor()));
227            if (!type.getArguments().isEmpty()) {
228                sb.append("<");
229                appendTypeProjections(type.getArguments(), sb);
230                sb.append(">");
231            }
232            if (type.isNullable()) {
233                sb.append("?");
234            }
235            return sb.toString();
236        }
237    
238        @NotNull
239        private String renderTypeName(@NotNull TypeConstructor typeConstructor) {
240            ClassifierDescriptor cd = typeConstructor.getDeclarationDescriptor();
241            if (cd instanceof TypeParameterDescriptor) {
242                return renderName(cd.getName());
243            }
244            else if (cd instanceof ClassDescriptor) {
245                return renderClassName((ClassDescriptor) cd);
246            }
247            else {
248                assert cd == null: "Unexpected classifier: " + cd.getClass();
249                return typeConstructor.toString();
250            }
251        }
252    
253        private void appendTypeProjections(@NotNull List<TypeProjection> typeProjections, @NotNull StringBuilder builder) {
254            for (Iterator<TypeProjection> iterator = typeProjections.iterator(); iterator.hasNext(); ) {
255                TypeProjection typeProjection = iterator.next();
256                if (typeProjection.getProjectionKind() != Variance.INVARIANT) {
257                    builder.append(typeProjection.getProjectionKind()).append(" ");
258                }
259                builder.append(renderType(typeProjection.getType()));
260                if (iterator.hasNext()) {
261                    builder.append(", ");
262                }
263            }
264        }
265    
266        @NotNull
267        private String renderFunctionType(@NotNull JetType type) {
268            StringBuilder sb = new StringBuilder();
269    
270            JetType receiverType = KotlinBuiltIns.getInstance().getReceiverType(type);
271            if (receiverType != null) {
272                sb.append(renderType(receiverType));
273                sb.append(".");
274            }
275    
276            sb.append("(");
277            appendTypeProjections(KotlinBuiltIns.getInstance().getParameterTypeProjectionsFromFunctionType(type), sb);
278            sb.append(") " + arrow() + " ");
279            sb.append(renderType(KotlinBuiltIns.getInstance().getReturnTypeFromFunctionType(type)));
280    
281            if (type.isNullable()) {
282                return "(" + sb + ")?";
283            }
284            return sb.toString();
285        }
286    
287    
288        /* METHODS FOR ALL KINDS OF DESCRIPTORS */
289        private void appendDefinedIn(@NotNull DeclarationDescriptor descriptor, @NotNull StringBuilder builder) {
290            if (descriptor instanceof ModuleDescriptor) {
291                builder.append(" is a module");
292                return;
293            }
294            builder.append(" ").append(renderMessage("defined in")).append(" ");
295    
296            DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
297            if (containingDeclaration != null) {
298                FqNameUnsafe fqName = DescriptorUtils.getFQName(containingDeclaration);
299                builder.append(FqName.ROOT.equalsTo(fqName) ? "root package" : renderFqName(fqName));
300            }
301        }
302    
303        private void renderAnnotations(@NotNull Annotated annotated, @NotNull StringBuilder builder) {
304            if (!modifiers.contains(Modifier.ANNOTATIONS)) return;
305            for (AnnotationDescriptor annotation : annotated.getAnnotations()) {
306                ClassDescriptor annotationClass = (ClassDescriptor) annotation.getType().getConstructor().getDeclarationDescriptor();
307                assert annotationClass != null;
308    
309                if (!excludedAnnotationClasses.contains(DescriptorUtils.getFQName(annotationClass).toSafe())) {
310                    builder.append(renderType(annotation.getType()));
311                    if (verbose) {
312                        builder.append("(").append(StringUtil.join(DescriptorUtils.getSortedValueArguments(annotation, this), ", ")).append(")");
313                    }
314                    builder.append(" ");
315                }
316            }
317        }
318    
319        private void renderVisibility(@NotNull Visibility visibility, @NotNull StringBuilder builder) {
320            if (!modifiers.contains(Modifier.VISIBILITY)) return;
321            if (normalizedVisibilities) {
322                visibility = visibility.normalize();
323            }
324            if (!showInternalKeyword && visibility == Visibilities.INTERNAL) return;
325            builder.append(renderKeyword(visibility.toString())).append(" ");
326        }
327    
328        private void renderModality(@NotNull Modality modality, @NotNull StringBuilder builder) {
329            if (!modifiers.contains(Modifier.MODALITY)) return;
330            String keyword = modality.name().toLowerCase();
331            builder.append(renderKeyword(keyword)).append(" ");
332        }
333    
334        private void renderInner(boolean isInner, @NotNull StringBuilder builder) {
335            if (!modifiers.contains(Modifier.INNER)) return;
336            if (isInner) {
337                builder.append(renderKeyword("inner")).append(" ");
338            }
339        }
340    
341        private void renderModalityForCallable(@NotNull CallableMemberDescriptor callable, @NotNull StringBuilder builder) {
342            if (!DescriptorUtils.isTopLevelDeclaration(callable) || callable.getModality() != Modality.FINAL) {
343                if (overridesSomething(callable)
344                    && overrideRenderingPolicy == OverrideRenderingPolicy.RENDER_OVERRIDE
345                    && callable.getModality() == Modality.OPEN) {
346                    return;
347                }
348                renderModality(callable.getModality(), builder);
349            }
350        }
351    
352        private boolean overridesSomething(CallableMemberDescriptor callable) {
353            return !callable.getOverriddenDescriptors().isEmpty();
354        }
355    
356        private void renderOverride(@NotNull CallableMemberDescriptor callableMember, @NotNull StringBuilder builder) {
357            if (!modifiers.contains(Modifier.OVERRIDE)) return;
358            if (overridesSomething(callableMember)) {
359                if (overrideRenderingPolicy != OverrideRenderingPolicy.RENDER_OPEN) {
360                    builder.append("override ");
361                    if (verbose) {
362                        builder.append("/*").append(callableMember.getOverriddenDescriptors().size()).append("*/ ");
363                    }
364                }
365            }
366        }
367    
368        private void renderMemberKind(CallableMemberDescriptor callableMember, StringBuilder builder) {
369            if (!modifiers.contains(Modifier.MEMBER_KIND)) return;
370            if (verbose && callableMember.getKind() != CallableMemberDescriptor.Kind.DECLARATION) {
371                builder.append("/*").append(callableMember.getKind().name().toLowerCase()).append("*/ ");
372            }
373        }
374    
375        @NotNull
376        @Override
377        public String render(@NotNull DeclarationDescriptor declarationDescriptor) {
378            StringBuilder stringBuilder = new StringBuilder();
379            declarationDescriptor.accept(new RenderDeclarationDescriptorVisitor(), stringBuilder);
380    
381            if (withDefinedIn) {
382                appendDefinedIn(declarationDescriptor, stringBuilder);
383            }
384            return stringBuilder.toString();
385        }
386    
387    
388        /* TYPE PARAMETERS */
389        private void renderTypeParameter(@NotNull TypeParameterDescriptor typeParameter, @NotNull StringBuilder builder, boolean topLevel) {
390            if (topLevel) {
391                builder.append(lt());
392            }
393    
394            if (verbose) {
395                builder.append("/*").append(typeParameter.getIndex()).append("*/ ");
396            }
397    
398            if (typeParameter.isReified()) {
399                builder.append(renderKeyword("reified")).append(" ");
400            }
401            String variance = typeParameter.getVariance().toString();
402            if (!variance.isEmpty()) {
403                builder.append(renderKeyword(variance)).append(" ");
404            }
405            renderName(typeParameter, builder);
406            int upperBoundsCount = typeParameter.getUpperBounds().size();
407            if ((upperBoundsCount > 1 && !topLevel) || upperBoundsCount == 1) {
408                JetType upperBound = typeParameter.getUpperBounds().iterator().next();
409                if (!KotlinBuiltIns.getInstance().getDefaultBound().equals(upperBound) || alwaysRenderAny) {
410                    builder.append(" : ").append(renderType(upperBound));
411                }
412            }
413            else if (topLevel) {
414                boolean first = true;
415                for (JetType upperBound : typeParameter.getUpperBounds()) {
416                    if (upperBound.equals(KotlinBuiltIns.getInstance().getDefaultBound())) {
417                        continue;
418                    }
419                    if (first) {
420                        builder.append(" : ");
421                    }
422                    else {
423                        builder.append(" & ");
424                    }
425                    builder.append(renderType(upperBound));
426                    first = false;
427                }
428            }
429            else {
430                // rendered with "where"
431            }
432    
433            if (topLevel) {
434                builder.append(">");
435            }
436        }
437    
438        private void renderTypeParameters(
439                @NotNull List<TypeParameterDescriptor> typeParameters,
440                @NotNull StringBuilder builder,
441                boolean withSpace
442        ) {
443            if (!typeParameters.isEmpty()) {
444                builder.append(lt());
445                for (Iterator<TypeParameterDescriptor> iterator = typeParameters.iterator(); iterator.hasNext(); ) {
446                    TypeParameterDescriptor typeParameterDescriptor = iterator.next();
447                    renderTypeParameter(typeParameterDescriptor, builder, false);
448                    if (iterator.hasNext()) {
449                        builder.append(", ");
450                    }
451                }
452                builder.append(">");
453                if (withSpace) {
454                    builder.append(" ");
455                }
456            }
457        }
458    
459        /* FUNCTIONS */
460        private void renderFunction(@NotNull FunctionDescriptor function, @NotNull StringBuilder builder) {
461            if (!startFromName) {
462                renderAnnotations(function, builder);
463                renderVisibility(function.getVisibility(), builder);
464                renderModalityForCallable(function, builder);
465                renderOverride(function, builder);
466                renderMemberKind(function, builder);
467    
468                builder.append(renderKeyword("fun")).append(" ");
469                renderTypeParameters(function.getTypeParameters(), builder, true);
470    
471                ReceiverParameterDescriptor receiver = function.getReceiverParameter();
472                if (receiver != null) {
473                    builder.append(escape(renderType(receiver.getType()))).append(".");
474                }
475            }
476    
477            renderName(function, builder);
478            renderValueParameters(function, builder);
479            JetType returnType = function.getReturnType();
480            if (unitReturnType || !KotlinBuiltIns.getInstance().isUnit(returnType)) {
481                builder.append(": ").append(returnType == null ? "[NULL]" : escape(renderType(returnType)));
482            }
483            renderWhereSuffix(function.getTypeParameters(), builder);
484        }
485    
486        private void renderConstructor(@NotNull ConstructorDescriptor constructor, @NotNull StringBuilder builder) {
487            renderAnnotations(constructor, builder);
488            renderVisibility(constructor.getVisibility(), builder);
489            renderMemberKind(constructor, builder);
490    
491            builder.append(renderKeyword("constructor")).append(" ");
492    
493            ClassDescriptor classDescriptor = constructor.getContainingDeclaration();
494            renderName(classDescriptor, builder);
495    
496            renderTypeParameters(classDescriptor.getTypeConstructor().getParameters(), builder, false);
497            renderValueParameters(constructor, builder);
498            renderWhereSuffix(constructor.getTypeParameters(), builder);
499        }
500    
501        private void renderWhereSuffix(@NotNull List<TypeParameterDescriptor> typeParameters, @NotNull StringBuilder builder) {
502            List<String> upperBoundStrings = Lists.newArrayList();
503    
504            for (TypeParameterDescriptor typeParameter : typeParameters) {
505                if (typeParameter.getUpperBounds().size() > 1) {
506                    boolean first = true;
507                    for (JetType upperBound : typeParameter.getUpperBounds()) {
508                        // first parameter is rendered by renderTypeParameter:
509                        if (!first) {
510                            upperBoundStrings.add(renderName(typeParameter.getName()) + " : " + escape(renderType(upperBound)));
511                        }
512                        first = false;
513                    }
514                }
515            }
516            if (!upperBoundStrings.isEmpty()) {
517                builder.append(" ").append(renderKeyword("where")).append(" ");
518                builder.append(StringUtil.join(upperBoundStrings, ", "));
519            }
520        }
521    
522        @NotNull
523        @Override
524        public String renderFunctionParameters(@NotNull FunctionDescriptor functionDescriptor) {
525            StringBuilder stringBuilder = new StringBuilder();
526            renderValueParameters(functionDescriptor, stringBuilder);
527            return stringBuilder.toString();
528        }
529    
530        private void renderValueParameters(@NotNull FunctionDescriptor function, @NotNull StringBuilder builder) {
531            handler.appendBeforeValueParameters(function, builder);
532            for (ValueParameterDescriptor parameter : function.getValueParameters()) {
533                handler.appendBeforeValueParameter(parameter, builder);
534                renderValueParameter(parameter, builder, false);
535                handler.appendAfterValueParameter(parameter, builder);
536            }
537            handler.appendAfterValueParameters(function, builder);
538        }
539    
540        /* VARIABLES */
541        private void renderValueParameter(@NotNull ValueParameterDescriptor valueParameter, @NotNull StringBuilder builder, boolean topLevel) {
542            if (topLevel) {
543                builder.append(renderKeyword("value-parameter")).append(" ");
544            }
545    
546            if (verbose) {
547                builder.append("/*").append(valueParameter.getIndex()).append("*/ ");
548            }
549    
550            renderAnnotations(valueParameter, builder);
551            renderVariable(valueParameter, builder, topLevel);
552            boolean withDefaultValue = debugMode ? valueParameter.declaresDefaultValue() : valueParameter.hasDefaultValue();
553            if (withDefaultValue) {
554                builder.append(" = ...");
555            }
556        }
557    
558        private void renderValVarPrefix(@NotNull VariableDescriptor variable, @NotNull StringBuilder builder) {
559            builder.append(renderKeyword(variable.isVar() ? "var" : "val")).append(" ");
560        }
561    
562        private void renderVariable(@NotNull VariableDescriptor variable, @NotNull StringBuilder builder, boolean topLevel) {
563            JetType realType = variable.getType();
564    
565            JetType varargElementType = variable instanceof ValueParameterDescriptor
566                                        ? ((ValueParameterDescriptor) variable).getVarargElementType()
567                                        : null;
568            JetType typeToRender = varargElementType != null ? varargElementType : realType;
569    
570            if (varargElementType != null) {
571                builder.append(renderKeyword("vararg")).append(" ");
572            }
573            if (topLevel && !startFromName) {
574                renderValVarPrefix(variable, builder);
575            }
576    
577            renderName(variable, builder);
578            builder.append(": ").append(escape(renderType(typeToRender)));
579    
580            if (verbose && varargElementType != null) {
581                builder.append(" /*").append(escape(renderType(realType))).append("*/");
582            }
583        }
584    
585        private void renderProperty(@NotNull PropertyDescriptor property, @NotNull StringBuilder builder) {
586            if (!startFromName) {
587                renderAnnotations(property, builder);
588                renderVisibility(property.getVisibility(), builder);
589                renderModalityForCallable(property, builder);
590                renderOverride(property, builder);
591                renderMemberKind(property, builder);
592    
593                renderValVarPrefix(property, builder);
594            }
595    
596            renderTypeParameters(property.getTypeParameters(), builder, true);
597    
598            ReceiverParameterDescriptor receiver = property.getReceiverParameter();
599            if (receiver != null) {
600                builder.append(escape(renderType(receiver.getType()))).append(".");
601            }
602            renderName(property, builder);
603            builder.append(": ").append(escape(renderType(property.getType())));
604    
605            renderWhereSuffix(property.getTypeParameters(), builder);
606        }
607    
608    
609        /* CLASSES */
610        private void renderClass(@NotNull ClassDescriptor klass, @NotNull StringBuilder builder) {
611            if (!startFromName) {
612                renderAnnotations(klass, builder);
613                renderVisibility(klass.getVisibility(), builder);
614                if (!(klass.getKind() == ClassKind.TRAIT && klass.getModality() == Modality.ABSTRACT
615                    || klass.getKind().isObject() && klass.getModality() == Modality.FINAL)) {
616                    renderModality(klass.getModality(), builder);
617                }
618                renderInner(klass.isInner(), builder);
619                builder.append(renderKeyword(getClassKindPrefix(klass)));
620            }
621    
622            if (klass.getKind() != ClassKind.CLASS_OBJECT || verbose) {
623                builder.append(" ");
624                renderName(klass, builder);
625            }
626    
627            List<TypeParameterDescriptor> typeParameters = klass.getTypeConstructor().getParameters();
628            renderTypeParameters(typeParameters, builder, false);
629    
630            if (!klass.getKind().isObject() && classWithPrimaryConstructor) {
631                ConstructorDescriptor primaryConstructor = klass.getUnsubstitutedPrimaryConstructor();
632                if (primaryConstructor != null) {
633                    renderValueParameters(primaryConstructor, builder);
634                }
635            }
636    
637            if (!klass.equals(KotlinBuiltIns.getInstance().getNothing())) {
638                Collection<JetType> supertypes = klass.getTypeConstructor().getSupertypes();
639                if (supertypes.isEmpty() || !alwaysRenderAny && supertypes.size() == 1 && KotlinBuiltIns.getInstance().isAny(supertypes.iterator().next())) {
640                }
641                else {
642                    builder.append(" : ");
643                    for (Iterator<JetType> iterator = supertypes.iterator(); iterator.hasNext(); ) {
644                        JetType supertype = iterator.next();
645                        builder.append(renderType(supertype));
646                        if (iterator.hasNext()) {
647                            builder.append(", ");
648                        }
649                    }
650                }
651            }
652    
653            renderWhereSuffix(typeParameters, builder);
654        }
655    
656        @NotNull
657        private static String getClassKindPrefix(@NotNull ClassDescriptor klass) {
658            switch (klass.getKind()) {
659                case CLASS:
660                    return "class";
661                case TRAIT:
662                    return "trait";
663                case ENUM_CLASS:
664                    return "enum class";
665                case OBJECT:
666                    return "object";
667                case ANNOTATION_CLASS:
668                    return "annotation class";
669                case CLASS_OBJECT:
670                    return "class object";
671                case ENUM_ENTRY:
672                    return "enum entry";
673                default:
674                    throw new IllegalStateException("unknown class kind: " + klass.getKind());
675            }
676        }
677    
678    
679        /* OTHER */
680        private void renderModuleOrScript(@NotNull DeclarationDescriptor moduleOrScript, @NotNull StringBuilder builder) {
681            renderName(moduleOrScript, builder);
682        }
683    
684        private void renderNamespace(@NotNull NamespaceDescriptor namespace, @NotNull StringBuilder builder) {
685            builder.append(renderKeyword("package")).append(" ");
686            renderName(namespace, builder);
687        }
688    
689    
690        /* STUPID DISPATCH-ONLY VISITOR */
691        private class RenderDeclarationDescriptorVisitor extends DeclarationDescriptorVisitorEmptyBodies<Void, StringBuilder> {
692            @Override
693            public Void visitValueParameterDescriptor(ValueParameterDescriptor descriptor, StringBuilder builder) {
694                renderValueParameter(descriptor, builder, true);
695                return null;
696            }
697    
698            @Override
699            public Void visitVariableDescriptor(VariableDescriptor descriptor, StringBuilder builder) {
700                renderVariable(descriptor, builder, true);
701                return null;
702            }
703    
704            @Override
705            public Void visitPropertyDescriptor(PropertyDescriptor descriptor, StringBuilder builder) {
706                renderProperty(descriptor, builder);
707                return null;
708            }
709    
710            @Override
711            public Void visitFunctionDescriptor(FunctionDescriptor descriptor, StringBuilder builder) {
712                renderFunction(descriptor, builder);
713                return null;
714            }
715    
716            @Override
717            public Void visitReceiverParameterDescriptor(ReceiverParameterDescriptor descriptor, StringBuilder data) {
718                throw new UnsupportedOperationException("Don't render receiver parameters");
719            }
720    
721            @Override
722            public Void visitConstructorDescriptor(ConstructorDescriptor constructorDescriptor, StringBuilder builder) {
723                renderConstructor(constructorDescriptor, builder);
724                return null;
725            }
726    
727            @Override
728            public Void visitTypeParameterDescriptor(TypeParameterDescriptor descriptor, StringBuilder builder) {
729                renderTypeParameter(descriptor, builder, true);
730                return null;
731            }
732    
733            @Override
734            public Void visitNamespaceDescriptor(NamespaceDescriptor namespaceDescriptor, StringBuilder builder) {
735                renderNamespace(namespaceDescriptor, builder);
736                return null;
737            }
738    
739            @Override
740            public Void visitModuleDeclaration(ModuleDescriptor descriptor, StringBuilder builder) {
741                renderModuleOrScript(descriptor, builder);
742                return null;
743            }
744    
745            @Override
746            public Void visitScriptDescriptor(ScriptDescriptor scriptDescriptor, StringBuilder builder) {
747                renderModuleOrScript(scriptDescriptor, builder);
748                return null;
749            }
750    
751            @Override
752            public Void visitClassDescriptor(ClassDescriptor descriptor, StringBuilder builder) {
753                renderClass(descriptor, builder);
754                return null;
755            }
756       }
757    }