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