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