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