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