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