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