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 Set<DescriptorRenderer.Modifier> 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                Set<DescriptorRenderer.Modifier> 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.contains(Modifier.ANNOTATIONS)) 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.contains(Modifier.VISIBILITY)) 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.contains(Modifier.MODALITY)) 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.contains(Modifier.INNER)) 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.contains(Modifier.OVERRIDE)) 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.contains(Modifier.MEMBER_KIND)) 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    }