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