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