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