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