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
017package org.jetbrains.jet.renderer;
018
019import com.google.common.collect.Lists;
020import com.google.common.collect.Sets;
021import com.intellij.openapi.util.text.StringUtil;
022import com.intellij.psi.tree.IElementType;
023import org.jetbrains.annotations.NotNull;
024import org.jetbrains.jet.lang.descriptors.*;
025import org.jetbrains.jet.lang.descriptors.annotations.Annotated;
026import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
027import org.jetbrains.jet.lang.descriptors.impl.DeclarationDescriptorVisitorEmptyBodies;
028import org.jetbrains.jet.lang.resolve.DescriptorUtils;
029import org.jetbrains.jet.lang.resolve.calls.CallResolverUtil;
030import org.jetbrains.jet.lang.resolve.name.FqName;
031import org.jetbrains.jet.lang.resolve.name.FqNameUnsafe;
032import org.jetbrains.jet.lang.resolve.name.Name;
033import org.jetbrains.jet.lang.types.*;
034import org.jetbrains.jet.lang.types.expressions.ExpressionTypingUtils;
035import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
036import org.jetbrains.jet.lexer.JetKeywordToken;
037import org.jetbrains.jet.lexer.JetTokens;
038
039import java.util.*;
040
041public 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}