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