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