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