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