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