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