001    /*
002     * Copyright 2010-2013 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 com.google.common.collect.Lists;
020    import com.google.common.collect.Sets;
021    import com.intellij.openapi.util.text.StringUtil;
022    import com.intellij.util.Function;
023    import org.jetbrains.annotations.NotNull;
024    import org.jetbrains.jet.lang.descriptors.*;
025    import org.jetbrains.jet.lang.descriptors.annotations.Annotated;
026    import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
027    import org.jetbrains.jet.lang.descriptors.annotations.DefaultAnnotationArgumentVisitor;
028    import org.jetbrains.jet.lang.descriptors.impl.DeclarationDescriptorVisitorEmptyBodies;
029    import org.jetbrains.jet.lang.resolve.DescriptorUtils;
030    import org.jetbrains.jet.lang.resolve.constants.AnnotationValue;
031    import org.jetbrains.jet.lang.resolve.constants.ArrayValue;
032    import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
033    import org.jetbrains.jet.lang.resolve.constants.JavaClassValue;
034    import org.jetbrains.jet.lang.resolve.name.FqName;
035    import org.jetbrains.jet.lang.resolve.name.FqNameBase;
036    import org.jetbrains.jet.lang.resolve.name.FqNameUnsafe;
037    import org.jetbrains.jet.lang.resolve.name.Name;
038    import org.jetbrains.jet.lang.types.*;
039    import org.jetbrains.jet.lang.types.error.MissingDependencyErrorClass;
040    import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
041    
042    import java.util.*;
043    
044    import static org.jetbrains.jet.lang.types.ErrorUtils.UninferredParameterType;
045    import static org.jetbrains.jet.lang.types.TypeUtils.*;
046    
047    public class DescriptorRendererImpl implements DescriptorRenderer {
048        private final boolean shortNames;
049        private final boolean withDefinedIn;
050        private final Set<DescriptorRenderer.Modifier> modifiers;
051        private final boolean startFromName;
052        private final boolean debugMode;
053        private final boolean classWithPrimaryConstructor;
054        private final boolean verbose;
055        private final boolean unitReturnType;
056        private final boolean normalizedVisibilities;
057        private final boolean showInternalKeyword;
058        private final boolean prettyFunctionTypes;
059        private final boolean uninferredTypeParameterAsName;
060        private final boolean includeSynthesizedParameterNames;
061    
062        @NotNull
063        private final OverrideRenderingPolicy overrideRenderingPolicy;
064        @NotNull
065        private final ValueParametersHandler handler;
066        @NotNull
067        private final TextFormat textFormat;
068        private final boolean includePropertyConstant;
069        @NotNull
070        private final Set<FqName> excludedAnnotationClasses;
071    
072        /* package */ DescriptorRendererImpl(
073                boolean shortNames,
074                boolean withDefinedIn,
075                Set<Modifier> modifiers,
076                boolean startFromName,
077                boolean debugMode,
078                boolean classWithPrimaryConstructor,
079                boolean verbose,
080                boolean unitReturnType,
081                boolean normalizedVisibilities,
082                boolean showInternalKeyword,
083                boolean prettyFunctionTypes,
084                boolean uninferredTypeParameterAsName,
085                @NotNull OverrideRenderingPolicy overrideRenderingPolicy,
086                @NotNull ValueParametersHandler handler,
087                @NotNull TextFormat textFormat,
088                @NotNull Collection<FqName> excludedAnnotationClasses,
089                boolean includePropertyConstant,
090                boolean includeSynthesizedParameterNames
091        ) {
092            this.shortNames = shortNames;
093            this.withDefinedIn = withDefinedIn;
094            this.modifiers = modifiers;
095            this.startFromName = startFromName;
096            this.handler = handler;
097            this.classWithPrimaryConstructor = classWithPrimaryConstructor;
098            this.verbose = verbose;
099            this.unitReturnType = unitReturnType;
100            this.normalizedVisibilities = normalizedVisibilities;
101            this.showInternalKeyword = showInternalKeyword;
102            this.overrideRenderingPolicy = overrideRenderingPolicy;
103            this.debugMode = debugMode;
104            this.textFormat = textFormat;
105            this.includePropertyConstant = includePropertyConstant;
106            this.excludedAnnotationClasses = Sets.newHashSet(excludedAnnotationClasses);
107            this.prettyFunctionTypes = prettyFunctionTypes;
108            this.uninferredTypeParameterAsName = uninferredTypeParameterAsName;
109            this.includeSynthesizedParameterNames = includeSynthesizedParameterNames;
110        }
111    
112        /* FORMATTING */
113        @NotNull
114        private String renderKeyword(@NotNull String keyword) {
115            switch (textFormat) {
116                case PLAIN:
117                    return keyword;
118                case HTML:
119                    return "<b>" + keyword + "</b>";
120            }
121            throw new IllegalStateException("Unexpected textFormat: " + textFormat);
122        }
123    
124        @NotNull
125        private String renderError(@NotNull String keyword) {
126            switch (textFormat) {
127                case PLAIN:
128                    return keyword;
129                case HTML:
130                    return "<font color=red><b>" + keyword + "</b></font>";
131            }
132            throw new IllegalStateException("Unexpected textFormat: " + textFormat);
133        }
134    
135        @NotNull
136        private String escape(@NotNull String string) {
137            switch (textFormat) {
138                case PLAIN:
139                    return string;
140                case HTML:
141                    return string.replaceAll("<", "&lt;").replaceAll(">", "&gt;");
142            }
143            throw new IllegalStateException("Unexpected textFormat: " + textFormat);
144        }
145    
146        @NotNull
147        private String lt() {
148            return escape("<");
149        }
150    
151        @NotNull
152        private String gt() {
153            return escape(">");
154        }
155    
156        @NotNull
157        private String arrow() {
158            switch (textFormat) {
159                case PLAIN:
160                    return escape("->");
161                case HTML:
162                    return "&rarr;";
163            }
164            throw new IllegalStateException("Unexpected textFormat: " + textFormat);
165        }
166    
167        @NotNull
168        private String renderMessage(@NotNull String message) {
169            switch (textFormat) {
170                case PLAIN:
171                    return message;
172                case HTML:
173                    return "<i>" + message + "</i>";
174            }
175            throw new IllegalStateException("Unexpected textFormat: " + textFormat);
176        }
177    
178    
179        /* NAMES RENDERING */
180        @NotNull
181        private String renderName(@NotNull Name identifier) {
182            String asString = identifier.toString();
183            return escape(KeywordStringsGenerated.KEYWORDS.contains(asString) ? '`' + asString + '`' : asString);
184        }
185    
186        private void renderName(@NotNull DeclarationDescriptor descriptor, @NotNull StringBuilder builder) {
187            builder.append(renderName(descriptor.getName()));
188        }
189    
190        @NotNull
191        private String renderFqName(@NotNull FqNameBase fqName) {
192            return renderFqName(fqName.pathSegments());
193        }
194    
195    
196        @NotNull
197        private String renderFqName(@NotNull List<Name> pathSegments) {
198            StringBuilder buf = new StringBuilder();
199            for (Name element : pathSegments) {
200                if (buf.length() != 0) {
201                    buf.append(".");
202                }
203                buf.append(renderName(element));
204            }
205            return buf.toString();
206        }
207    
208        @NotNull
209        private String renderClassName(@NotNull ClassDescriptor klass) {
210            if (klass instanceof MissingDependencyErrorClass) {
211                return ((MissingDependencyErrorClass) klass).getFullFqName().asString();
212            }
213            if (ErrorUtils.isError(klass)) {
214                return klass.getTypeConstructor().toString();
215            }
216            if (shortNames) {
217                List<Name> qualifiedNameElements = Lists.newArrayList();
218    
219                // for nested classes qualified name should be used
220                DeclarationDescriptor current = klass;
221                do {
222                    if (((ClassDescriptor) current).getKind() != ClassKind.CLASS_OBJECT) {
223                        qualifiedNameElements.add(current.getName());
224                    }
225                    current = current.getContainingDeclaration();
226                }
227                while (current instanceof ClassDescriptor);
228    
229                Collections.reverse(qualifiedNameElements);
230                return renderFqName(qualifiedNameElements);
231            }
232            return renderFqName(DescriptorUtils.getFqName(klass));
233        }
234    
235        /* TYPES RENDERING */
236        @NotNull
237        @Override
238        public String renderType(@NotNull JetType type) {
239            if (type == CANT_INFER_LAMBDA_PARAM_TYPE || type == DONT_CARE) {
240                return "???";
241            }
242            if (ErrorUtils.isUninferredParameter(type)) {
243                if (uninferredTypeParameterAsName) {
244                    return renderError(((UninferredParameterType) type).getTypeParameterDescriptor().getName().toString());
245                }
246                return "???";
247            }
248            if (type instanceof PackageType) {
249                return type.toString();
250            }
251            if (type instanceof LazyType && debugMode) {
252                return type.toString();
253            }
254            if (type.isError()) {
255                return renderDefaultType(type);
256            }
257            if (KotlinBuiltIns.getInstance().isExactFunctionOrExtensionFunctionType(type) && prettyFunctionTypes) {
258                return renderFunctionType(type);
259            }
260            return renderDefaultType(type);
261        }
262    
263        @NotNull
264        @Override
265        public String renderTypeArguments(@NotNull List<TypeProjection> typeArguments) {
266            if (typeArguments.isEmpty()) return "";
267            StringBuilder sb = new StringBuilder();
268            sb.append(lt());
269            appendTypeProjections(typeArguments, sb);
270            sb.append(gt());
271            return sb.toString();
272        }
273    
274        @NotNull
275        private String renderDefaultType(@NotNull JetType type) {
276            StringBuilder sb = new StringBuilder();
277    
278            if (type.isError()) {
279                sb.append(type.getConstructor().toString()); // Debug name of an error type is more informative
280            }
281            else {
282                sb.append(renderTypeName(type.getConstructor()));
283            }
284            sb.append(renderTypeArguments(type.getArguments()));
285            if (type.isNullable()) {
286                sb.append("?");
287            }
288            return sb.toString();
289        }
290    
291        @NotNull
292        private String renderTypeName(@NotNull TypeConstructor typeConstructor) {
293            ClassifierDescriptor cd = typeConstructor.getDeclarationDescriptor();
294            if (cd instanceof TypeParameterDescriptor) {
295                return renderName(cd.getName());
296            }
297            else if (cd instanceof ClassDescriptor) {
298                return renderClassName((ClassDescriptor) cd);
299            }
300            else {
301                assert cd == null: "Unexpected classifier: " + cd.getClass();
302                return typeConstructor.toString();
303            }
304        }
305    
306        private void appendTypeProjections(@NotNull List<TypeProjection> typeProjections, @NotNull StringBuilder builder) {
307            for (Iterator<TypeProjection> iterator = typeProjections.iterator(); iterator.hasNext(); ) {
308                TypeProjection typeProjection = iterator.next();
309                if (typeProjection.getProjectionKind() != Variance.INVARIANT) {
310                    builder.append(typeProjection.getProjectionKind()).append(" ");
311                }
312                builder.append(renderType(typeProjection.getType()));
313                if (iterator.hasNext()) {
314                    builder.append(", ");
315                }
316            }
317        }
318    
319        @NotNull
320        private String renderFunctionType(@NotNull JetType type) {
321            StringBuilder sb = new StringBuilder();
322    
323            JetType receiverType = KotlinBuiltIns.getInstance().getReceiverType(type);
324            if (receiverType != null) {
325                sb.append(renderType(receiverType));
326                sb.append(".");
327            }
328    
329            sb.append("(");
330            appendTypeProjections(KotlinBuiltIns.getInstance().getParameterTypeProjectionsFromFunctionType(type), sb);
331            sb.append(") ").append(arrow()).append(" ");
332            sb.append(renderType(KotlinBuiltIns.getInstance().getReturnTypeFromFunctionType(type)));
333    
334            if (type.isNullable()) {
335                return "(" + sb + ")?";
336            }
337            return sb.toString();
338        }
339    
340    
341        /* METHODS FOR ALL KINDS OF DESCRIPTORS */
342        private void appendDefinedIn(@NotNull DeclarationDescriptor descriptor, @NotNull StringBuilder builder) {
343            if (descriptor instanceof PackageFragmentDescriptor || descriptor instanceof PackageViewDescriptor) {
344                return;
345            }
346            if (descriptor instanceof ModuleDescriptor) {
347                builder.append(" is a module");
348                return;
349            }
350            builder.append(" ").append(renderMessage("defined in")).append(" ");
351    
352            DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
353            if (containingDeclaration != null) {
354                FqNameUnsafe fqName = DescriptorUtils.getFqName(containingDeclaration);
355                builder.append(FqName.ROOT.equalsTo(fqName) ? "root package" : renderFqName(fqName));
356            }
357        }
358    
359        private void renderAnnotations(@NotNull Annotated annotated, @NotNull StringBuilder builder) {
360            if (!modifiers.contains(Modifier.ANNOTATIONS)) return;
361            for (AnnotationDescriptor annotation : annotated.getAnnotations()) {
362                ClassDescriptor annotationClass = (ClassDescriptor) annotation.getType().getConstructor().getDeclarationDescriptor();
363                assert annotationClass != null;
364    
365                if (!excludedAnnotationClasses.contains(DescriptorUtils.getFqNameSafe(annotationClass))) {
366                    builder.append(renderAnnotation(annotation)).append(" ");
367                }
368            }
369        }
370    
371        @Override
372        @NotNull
373        public String renderAnnotation(@NotNull AnnotationDescriptor annotation) {
374            StringBuilder sb = new StringBuilder();
375            sb.append(renderType(annotation.getType()));
376            if (verbose) {
377                sb.append("(").append(StringUtil.join(renderAndSortAnnotationArguments(annotation), ", ")).append(")");
378            }
379            return sb.toString();
380        }
381    
382        @NotNull
383        private List<String> renderAndSortAnnotationArguments(@NotNull AnnotationDescriptor descriptor) {
384            List<String> resultList = Lists.newArrayList();
385            for (Map.Entry<ValueParameterDescriptor, CompileTimeConstant<?>> entry : descriptor.getAllValueArguments().entrySet()) {
386                CompileTimeConstant<?> value = entry.getValue();
387                String typeSuffix = ": " + renderType(value.getType(KotlinBuiltIns.getInstance()));
388                resultList.add(entry.getKey().getName().asString() + " = " + renderConstant(value) + typeSuffix);
389            }
390            Collections.sort(resultList);
391            return resultList;
392        }
393    
394        @NotNull
395        private String renderConstant(@NotNull CompileTimeConstant<?> value) {
396            return value.accept(
397                    new DefaultAnnotationArgumentVisitor<String, Void>() {
398                        @Override
399                        public String visitValue(@NotNull CompileTimeConstant<?> value, Void data) {
400                            return value.toString();
401                        }
402    
403                        @Override
404                        public String visitArrayValue(ArrayValue value, Void data) {
405                            return "{" +
406                                   StringUtil.join(
407                                    value.getValue(),
408                                    new Function<CompileTimeConstant<?>, String>() {
409                                        @Override
410                                        public String fun(CompileTimeConstant<?> constant) {
411                                            return renderConstant(constant);
412                                        }
413                                    },
414                                    ", ") +
415                                   "}";
416                        }
417    
418                        @Override
419                        public String visitAnnotationValue(AnnotationValue value, Void data) {
420                            return renderAnnotation(value.getValue());
421                        }
422    
423                        @Override
424                        public String visitJavaClassValue(JavaClassValue value, Void data) {
425                            return renderType(value.getValue()) + ".class";
426                        }
427                    },
428                    null
429            );
430        }
431    
432        private void renderVisibility(@NotNull Visibility visibility, @NotNull StringBuilder builder) {
433            if (!modifiers.contains(Modifier.VISIBILITY)) return;
434            if (normalizedVisibilities) {
435                visibility = visibility.normalize();
436            }
437            if (!showInternalKeyword && visibility == Visibilities.INTERNAL) return;
438            builder.append(renderKeyword(visibility.toString())).append(" ");
439        }
440    
441        private void renderModality(@NotNull Modality modality, @NotNull StringBuilder builder) {
442            if (!modifiers.contains(Modifier.MODALITY)) return;
443            String keyword = modality.name().toLowerCase();
444            builder.append(renderKeyword(keyword)).append(" ");
445        }
446    
447        private void renderInner(boolean isInner, @NotNull StringBuilder builder) {
448            if (!modifiers.contains(Modifier.INNER)) return;
449            if (isInner) {
450                builder.append(renderKeyword("inner")).append(" ");
451            }
452        }
453    
454        private void renderModalityForCallable(@NotNull CallableMemberDescriptor callable, @NotNull StringBuilder builder) {
455            if (!DescriptorUtils.isTopLevelDeclaration(callable) || callable.getModality() != Modality.FINAL) {
456                if (overridesSomething(callable)
457                    && overrideRenderingPolicy == OverrideRenderingPolicy.RENDER_OVERRIDE
458                    && callable.getModality() == Modality.OPEN) {
459                    return;
460                }
461                renderModality(callable.getModality(), builder);
462            }
463        }
464    
465        private boolean overridesSomething(CallableMemberDescriptor callable) {
466            return !callable.getOverriddenDescriptors().isEmpty();
467        }
468    
469        private void renderOverride(@NotNull CallableMemberDescriptor callableMember, @NotNull StringBuilder builder) {
470            if (!modifiers.contains(Modifier.OVERRIDE)) return;
471            if (overridesSomething(callableMember)) {
472                if (overrideRenderingPolicy != OverrideRenderingPolicy.RENDER_OPEN) {
473                    builder.append("override ");
474                    if (verbose) {
475                        builder.append("/*").append(callableMember.getOverriddenDescriptors().size()).append("*/ ");
476                    }
477                }
478            }
479        }
480    
481        private void renderMemberKind(CallableMemberDescriptor callableMember, StringBuilder builder) {
482            if (!modifiers.contains(Modifier.MEMBER_KIND)) return;
483            if (verbose && callableMember.getKind() != CallableMemberDescriptor.Kind.DECLARATION) {
484                builder.append("/*").append(callableMember.getKind().name().toLowerCase()).append("*/ ");
485            }
486        }
487    
488        @NotNull
489        @Override
490        public String render(@NotNull DeclarationDescriptor declarationDescriptor) {
491            StringBuilder stringBuilder = new StringBuilder();
492            declarationDescriptor.accept(new RenderDeclarationDescriptorVisitor(), stringBuilder);
493    
494            if (withDefinedIn) {
495                appendDefinedIn(declarationDescriptor, stringBuilder);
496            }
497            return stringBuilder.toString();
498        }
499    
500    
501        /* TYPE PARAMETERS */
502        private void renderTypeParameter(@NotNull TypeParameterDescriptor typeParameter, @NotNull StringBuilder builder, boolean topLevel) {
503            if (topLevel) {
504                builder.append(lt());
505            }
506    
507            if (verbose) {
508                builder.append("/*").append(typeParameter.getIndex()).append("*/ ");
509            }
510    
511            if (typeParameter.isReified()) {
512                builder.append(renderKeyword("reified")).append(" ");
513            }
514            String variance = typeParameter.getVariance().toString();
515            if (!variance.isEmpty()) {
516                builder.append(renderKeyword(variance)).append(" ");
517            }
518            renderName(typeParameter, builder);
519            int upperBoundsCount = typeParameter.getUpperBounds().size();
520            if ((upperBoundsCount > 1 && !topLevel) || upperBoundsCount == 1) {
521                JetType upperBound = typeParameter.getUpperBounds().iterator().next();
522                if (!KotlinBuiltIns.getInstance().getDefaultBound().equals(upperBound)) {
523                    builder.append(" : ").append(renderType(upperBound));
524                }
525            }
526            else if (topLevel) {
527                boolean first = true;
528                for (JetType upperBound : typeParameter.getUpperBounds()) {
529                    if (upperBound.equals(KotlinBuiltIns.getInstance().getDefaultBound())) {
530                        continue;
531                    }
532                    if (first) {
533                        builder.append(" : ");
534                    }
535                    else {
536                        builder.append(" & ");
537                    }
538                    builder.append(renderType(upperBound));
539                    first = false;
540                }
541            }
542            else {
543                // rendered with "where"
544            }
545    
546            if (topLevel) {
547                builder.append(gt());
548            }
549        }
550    
551        private void renderTypeParameters(
552                @NotNull List<TypeParameterDescriptor> typeParameters,
553                @NotNull StringBuilder builder,
554                boolean withSpace
555        ) {
556            if (!typeParameters.isEmpty()) {
557                builder.append(lt());
558                for (Iterator<TypeParameterDescriptor> iterator = typeParameters.iterator(); iterator.hasNext(); ) {
559                    TypeParameterDescriptor typeParameterDescriptor = iterator.next();
560                    renderTypeParameter(typeParameterDescriptor, builder, false);
561                    if (iterator.hasNext()) {
562                        builder.append(", ");
563                    }
564                }
565                builder.append(gt());
566                if (withSpace) {
567                    builder.append(" ");
568                }
569            }
570        }
571    
572        /* FUNCTIONS */
573        private void renderFunction(@NotNull FunctionDescriptor function, @NotNull StringBuilder builder) {
574            if (!startFromName) {
575                renderAnnotations(function, builder);
576                renderVisibility(function.getVisibility(), builder);
577                renderModalityForCallable(function, builder);
578                renderOverride(function, builder);
579                renderMemberKind(function, builder);
580    
581                builder.append(renderKeyword("fun")).append(" ");
582                renderTypeParameters(function.getTypeParameters(), builder, true);
583    
584                ReceiverParameterDescriptor receiver = function.getReceiverParameter();
585                if (receiver != null) {
586                    builder.append(escape(renderType(receiver.getType()))).append(".");
587                }
588            }
589    
590            renderName(function, builder);
591    
592            renderValueParameters(function, builder);
593    
594            JetType returnType = function.getReturnType();
595            if (unitReturnType || !KotlinBuiltIns.getInstance().isUnit(returnType)) {
596                builder.append(": ").append(returnType == null ? "[NULL]" : escape(renderType(returnType)));
597            }
598            renderWhereSuffix(function.getTypeParameters(), builder);
599        }
600    
601        private void renderConstructor(@NotNull ConstructorDescriptor constructor, @NotNull StringBuilder builder) {
602            renderAnnotations(constructor, builder);
603            renderVisibility(constructor.getVisibility(), builder);
604            renderMemberKind(constructor, builder);
605    
606            builder.append(renderKeyword("constructor")).append(" ");
607    
608            ClassDescriptor classDescriptor = constructor.getContainingDeclaration();
609            renderName(classDescriptor, builder);
610    
611            renderTypeParameters(classDescriptor.getTypeConstructor().getParameters(), builder, false);
612            renderValueParameters(constructor, builder);
613            renderWhereSuffix(constructor.getTypeParameters(), builder);
614        }
615    
616        private void renderWhereSuffix(@NotNull List<TypeParameterDescriptor> typeParameters, @NotNull StringBuilder builder) {
617            List<String> upperBoundStrings = Lists.newArrayList();
618    
619            for (TypeParameterDescriptor typeParameter : typeParameters) {
620                if (typeParameter.getUpperBounds().size() > 1) {
621                    boolean first = true;
622                    for (JetType upperBound : typeParameter.getUpperBounds()) {
623                        // first parameter is rendered by renderTypeParameter:
624                        if (!first) {
625                            upperBoundStrings.add(renderName(typeParameter.getName()) + " : " + escape(renderType(upperBound)));
626                        }
627                        first = false;
628                    }
629                }
630            }
631            if (!upperBoundStrings.isEmpty()) {
632                builder.append(" ").append(renderKeyword("where")).append(" ");
633                builder.append(StringUtil.join(upperBoundStrings, ", "));
634            }
635        }
636    
637        @NotNull
638        @Override
639        public String renderFunctionParameters(@NotNull FunctionDescriptor functionDescriptor) {
640            StringBuilder stringBuilder = new StringBuilder();
641            renderValueParameters(functionDescriptor, stringBuilder);
642            return stringBuilder.toString();
643        }
644    
645        private void renderValueParameters(@NotNull FunctionDescriptor function, @NotNull StringBuilder builder) {
646            boolean includeNames = includeSynthesizedParameterNames || !function.hasSynthesizedParameterNames();
647            handler.appendBeforeValueParameters(function, builder);
648            for (ValueParameterDescriptor parameter : function.getValueParameters()) {
649                handler.appendBeforeValueParameter(parameter, builder);
650                renderValueParameter(parameter, includeNames, builder, false);
651                handler.appendAfterValueParameter(parameter, builder);
652            }
653            handler.appendAfterValueParameters(function, builder);
654        }
655    
656        /* VARIABLES */
657        private void renderValueParameter(@NotNull ValueParameterDescriptor valueParameter, boolean includeName, @NotNull StringBuilder builder, boolean topLevel) {
658            if (topLevel) {
659                builder.append(renderKeyword("value-parameter")).append(" ");
660            }
661    
662            if (verbose) {
663                builder.append("/*").append(valueParameter.getIndex()).append("*/ ");
664            }
665    
666            renderAnnotations(valueParameter, builder);
667            renderVariable(valueParameter, includeName, builder, topLevel);
668            boolean withDefaultValue = debugMode ? valueParameter.declaresDefaultValue() : valueParameter.hasDefaultValue();
669            if (withDefaultValue) {
670                builder.append(" = ...");
671            }
672        }
673    
674        private void renderValVarPrefix(@NotNull VariableDescriptor variable, @NotNull StringBuilder builder) {
675            builder.append(renderKeyword(variable.isVar() ? "var" : "val")).append(" ");
676        }
677    
678        private void renderVariable(@NotNull VariableDescriptor variable, boolean includeName, @NotNull StringBuilder builder, boolean topLevel) {
679            JetType realType = variable.getType();
680    
681            JetType varargElementType = variable instanceof ValueParameterDescriptor
682                                        ? ((ValueParameterDescriptor) variable).getVarargElementType()
683                                        : null;
684            JetType typeToRender = varargElementType != null ? varargElementType : realType;
685    
686            if (varargElementType != null) {
687                builder.append(renderKeyword("vararg")).append(" ");
688            }
689            if (topLevel && !startFromName) {
690                renderValVarPrefix(variable, builder);
691            }
692    
693            if (includeName) {
694                renderName(variable, builder);
695                builder.append(": ");
696            }
697    
698            builder.append(escape(renderType(typeToRender)));
699    
700            renderInitializer(variable, builder);
701    
702            if (verbose && varargElementType != null) {
703                builder.append(" /*").append(escape(renderType(realType))).append("*/");
704            }
705        }
706    
707        private void renderProperty(@NotNull PropertyDescriptor property, @NotNull StringBuilder builder) {
708            if (!startFromName) {
709                renderAnnotations(property, builder);
710                renderVisibility(property.getVisibility(), builder);
711                renderModalityForCallable(property, builder);
712                renderOverride(property, builder);
713                renderMemberKind(property, builder);
714    
715                renderValVarPrefix(property, builder);
716            }
717    
718            renderTypeParameters(property.getTypeParameters(), builder, true);
719    
720            ReceiverParameterDescriptor receiver = property.getReceiverParameter();
721            if (receiver != null) {
722                builder.append(escape(renderType(receiver.getType()))).append(".");
723            }
724            renderName(property, builder);
725            builder.append(": ").append(escape(renderType(property.getType())));
726    
727            renderInitializer(property, builder);
728    
729            renderWhereSuffix(property.getTypeParameters(), builder);
730        }
731    
732        private void renderInitializer(@NotNull VariableDescriptor variable, @NotNull StringBuilder builder) {
733            if (includePropertyConstant) {
734                CompileTimeConstant<?> initializer = variable.getCompileTimeInitializer();
735                if (initializer != null) {
736                    builder.append(" = ").append(escape(renderConstant(initializer)));
737                }
738            }
739        }
740    
741        /* CLASSES */
742        private void renderClass(@NotNull ClassDescriptor klass, @NotNull StringBuilder builder) {
743            if (!startFromName) {
744                renderAnnotations(klass, builder);
745                renderVisibility(klass.getVisibility(), builder);
746                if (!(klass.getKind() == ClassKind.TRAIT && klass.getModality() == Modality.ABSTRACT
747                    || klass.getKind().isSingleton() && klass.getModality() == Modality.FINAL)) {
748                    renderModality(klass.getModality(), builder);
749                }
750                renderInner(klass.isInner(), builder);
751                builder.append(renderKeyword(getClassKindPrefix(klass)));
752            }
753    
754            if (klass.getKind() != ClassKind.CLASS_OBJECT || verbose) {
755                builder.append(" ");
756                renderName(klass, builder);
757            }
758    
759            List<TypeParameterDescriptor> typeParameters = klass.getTypeConstructor().getParameters();
760            renderTypeParameters(typeParameters, builder, false);
761    
762            if (!klass.getKind().isSingleton() && classWithPrimaryConstructor) {
763                ConstructorDescriptor primaryConstructor = klass.getUnsubstitutedPrimaryConstructor();
764                if (primaryConstructor != null) {
765                    renderValueParameters(primaryConstructor, builder);
766                }
767            }
768    
769            if (!klass.equals(KotlinBuiltIns.getInstance().getNothing())) {
770                Collection<JetType> supertypes = klass.getTypeConstructor().getSupertypes();
771                if (supertypes.isEmpty() || supertypes.size() == 1 && KotlinBuiltIns.getInstance().isAnyOrNullableAny(
772                        supertypes.iterator().next())) {
773                }
774                else {
775                    builder.append(" : ");
776                    for (Iterator<JetType> iterator = supertypes.iterator(); iterator.hasNext(); ) {
777                        JetType supertype = iterator.next();
778                        builder.append(renderType(supertype));
779                        if (iterator.hasNext()) {
780                            builder.append(", ");
781                        }
782                    }
783                }
784            }
785    
786            renderWhereSuffix(typeParameters, builder);
787        }
788    
789        @NotNull
790        private static String getClassKindPrefix(@NotNull ClassDescriptor klass) {
791            switch (klass.getKind()) {
792                case CLASS:
793                    return "class";
794                case TRAIT:
795                    return "trait";
796                case ENUM_CLASS:
797                    return "enum class";
798                case OBJECT:
799                    return "object";
800                case ANNOTATION_CLASS:
801                    return "annotation class";
802                case CLASS_OBJECT:
803                    return "class object";
804                case ENUM_ENTRY:
805                    return "enum entry";
806                default:
807                    throw new IllegalStateException("unknown class kind: " + klass.getKind());
808            }
809        }
810    
811    
812        /* OTHER */
813        private void renderModuleOrScript(@NotNull DeclarationDescriptor moduleOrScript, @NotNull StringBuilder builder) {
814            renderName(moduleOrScript, builder);
815        }
816    
817        private void renderPackageView(@NotNull PackageViewDescriptor packageView, @NotNull StringBuilder builder) {
818            builder.append(renderKeyword("package")).append(" ");
819            builder.append(renderFqName(packageView.getFqName()));
820            if (debugMode) {
821                builder.append(" in context of ");
822                renderName(packageView.getModule(), builder);
823            }
824        }
825    
826        private void renderPackageFragment(@NotNull PackageFragmentDescriptor fragment, @NotNull StringBuilder builder) {
827            builder.append(renderKeyword("package-fragment")).append(" ");
828            builder.append(renderFqName(fragment.getFqName()));
829            if (debugMode) {
830                builder.append(" in ");
831                renderName(fragment.getContainingDeclaration(), builder);
832            }
833        }
834    
835    
836        /* STUPID DISPATCH-ONLY VISITOR */
837        private class RenderDeclarationDescriptorVisitor extends DeclarationDescriptorVisitorEmptyBodies<Void, StringBuilder> {
838            @Override
839            public Void visitValueParameterDescriptor(ValueParameterDescriptor descriptor, StringBuilder builder) {
840                renderValueParameter(descriptor, true, builder, true);
841                return null;
842            }
843    
844            @Override
845            public Void visitVariableDescriptor(VariableDescriptor descriptor, StringBuilder builder) {
846                renderVariable(descriptor, true, builder, true);
847                return null;
848            }
849    
850            @Override
851            public Void visitPropertyDescriptor(PropertyDescriptor descriptor, StringBuilder builder) {
852                renderProperty(descriptor, builder);
853                return null;
854            }
855    
856            @Override
857            public Void visitFunctionDescriptor(FunctionDescriptor descriptor, StringBuilder builder) {
858                renderFunction(descriptor, builder);
859                return null;
860            }
861    
862            @Override
863            public Void visitReceiverParameterDescriptor(ReceiverParameterDescriptor descriptor, StringBuilder data) {
864                throw new UnsupportedOperationException("Don't render receiver parameters");
865            }
866    
867            @Override
868            public Void visitConstructorDescriptor(ConstructorDescriptor constructorDescriptor, StringBuilder builder) {
869                renderConstructor(constructorDescriptor, builder);
870                return null;
871            }
872    
873            @Override
874            public Void visitTypeParameterDescriptor(TypeParameterDescriptor descriptor, StringBuilder builder) {
875                renderTypeParameter(descriptor, builder, true);
876                return null;
877            }
878    
879            @Override
880            public Void visitPackageFragmentDescriptor(
881                    PackageFragmentDescriptor descriptor, StringBuilder builder
882            ) {
883                renderPackageFragment(descriptor, builder);
884                return null;
885            }
886    
887            @Override
888            public Void visitPackageViewDescriptor(
889                    PackageViewDescriptor descriptor, StringBuilder builder
890            ) {
891                renderPackageView(descriptor, builder);
892                return null;
893            }
894    
895            @Override
896            public Void visitModuleDeclaration(ModuleDescriptor descriptor, StringBuilder builder) {
897                renderModuleOrScript(descriptor, builder);
898                return null;
899            }
900    
901            @Override
902            public Void visitScriptDescriptor(ScriptDescriptor scriptDescriptor, StringBuilder builder) {
903                renderModuleOrScript(scriptDescriptor, builder);
904                return null;
905            }
906    
907            @Override
908            public Void visitClassDescriptor(ClassDescriptor descriptor, StringBuilder builder) {
909                renderClass(descriptor, builder);
910                return null;
911            }
912       }
913    }