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