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.lang.diagnostics.rendering;
018
019 import com.google.common.base.Predicate;
020 import com.google.common.collect.Lists;
021 import com.google.common.collect.Sets;
022 import com.intellij.openapi.diagnostic.Logger;
023 import com.intellij.openapi.util.text.StringUtil;
024 import com.intellij.psi.PsiElement;
025 import com.intellij.util.Function;
026 import org.jetbrains.annotations.NotNull;
027 import org.jetbrains.annotations.Nullable;
028 import org.jetbrains.jet.lang.descriptors.*;
029 import org.jetbrains.jet.lang.psi.JetClass;
030 import org.jetbrains.jet.lang.psi.JetClassOrObject;
031 import org.jetbrains.jet.lang.psi.JetNamedDeclaration;
032 import org.jetbrains.jet.lang.resolve.DescriptorUtils;
033 import org.jetbrains.jet.lang.resolve.calls.inference.*;
034 import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
035 import org.jetbrains.jet.lang.resolve.name.Name;
036 import org.jetbrains.jet.lang.types.JetType;
037 import org.jetbrains.jet.lang.types.TypeSubstitutor;
038 import org.jetbrains.jet.lang.types.TypeUtils;
039 import org.jetbrains.jet.lang.types.Variance;
040 import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
041 import org.jetbrains.jet.renderer.DescriptorRenderer;
042 import org.jetbrains.jet.renderer.Renderer;
043
044 import java.util.Collection;
045 import java.util.Iterator;
046 import java.util.List;
047 import java.util.Set;
048
049 import static org.jetbrains.jet.lang.diagnostics.rendering.TabledDescriptorRenderer.*;
050 import static org.jetbrains.jet.lang.resolve.calls.inference.TypeBounds.BoundKind.LOWER_BOUND;
051 import static org.jetbrains.jet.lang.resolve.calls.inference.TypeBounds.BoundKind.UPPER_BOUND;
052
053 public class Renderers {
054 private static final Logger LOG = Logger.getInstance(Renderers.class);
055
056 public static final Renderer<Object> TO_STRING = new Renderer<Object>() {
057 @NotNull
058 @Override
059 public String render(@NotNull Object element) {
060 if (element instanceof DeclarationDescriptor) {
061 LOG.warn("Diagnostic renderer TO_STRING was used to render an instance of DeclarationDescriptor.\n" +
062 "This is usually a bad idea, because descriptors' toString() includes some debug information, " +
063 "which should not be seen by the user.\nDescriptor: " + element);
064 }
065 return element.toString();
066 }
067
068 @Override
069 public String toString() {
070 return "TO_STRING";
071 }
072 };
073
074 public static final Renderer<String> STRING = new Renderer<String>() {
075 @NotNull
076 @Override
077 public String render(@NotNull String element) {
078 return element;
079 }
080 };
081
082 public static final Renderer<Named> NAME = new Renderer<Named>() {
083 @NotNull
084 @Override
085 public String render(@NotNull Named element) {
086 return element.getName().asString();
087 }
088 };
089
090 public static final Renderer<PsiElement> ELEMENT_TEXT = new Renderer<PsiElement>() {
091 @NotNull
092 @Override
093 public String render(@NotNull PsiElement element) {
094 return element.getText();
095 }
096 };
097
098 public static final Renderer<JetNamedDeclaration> DECLARATION_NAME = new Renderer<JetNamedDeclaration>() {
099 @NotNull
100 @Override
101 public String render(@NotNull JetNamedDeclaration element) {
102 return element.getNameAsSafeName().asString();
103 }
104 };
105
106 public static final Renderer<JetClassOrObject> RENDER_CLASS_OR_OBJECT = new Renderer<JetClassOrObject>() {
107 @NotNull
108 @Override
109 public String render(@NotNull JetClassOrObject classOrObject) {
110 String name = classOrObject.getName() != null ? " '" + classOrObject.getName() + "'" : "";
111 if (classOrObject instanceof JetClass) {
112 return "Class" + name;
113 }
114 return "Object" + name;
115 }
116 };
117
118 public static final Renderer<ClassDescriptor> RENDER_CLASS_OR_OBJECT_NAME = new Renderer<ClassDescriptor>() {
119 @NotNull
120 @Override
121 public String render(@NotNull ClassDescriptor classifier) {
122 return RenderingPackage.renderKindWithName(classifier);
123 }
124 };
125
126 public static final Renderer<JetType> RENDER_TYPE = new Renderer<JetType>() {
127 @NotNull
128 @Override
129 public String render(@NotNull JetType type) {
130 return DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(type);
131 }
132 };
133
134 public static final Renderer<Collection<? extends ResolvedCall<?>>> AMBIGUOUS_CALLS =
135 new Renderer<Collection<? extends ResolvedCall<?>>>() {
136 @NotNull
137 @Override
138 public String render(@NotNull Collection<? extends ResolvedCall<?>> argument) {
139 StringBuilder stringBuilder = new StringBuilder("\n");
140 for (ResolvedCall<?> call : argument) {
141 stringBuilder.append(DescriptorRenderer.FQ_NAMES_IN_TYPES.render(call.getResultingDescriptor())).append("\n");
142 }
143 return stringBuilder.toString();
144 }
145 };
146
147 public static <T> Renderer<Collection<? extends T>> commaSeparated(final Renderer<T> itemRenderer) {
148 return new Renderer<Collection<? extends T>>() {
149 @NotNull
150 @Override
151 public String render(@NotNull Collection<? extends T> object) {
152 StringBuilder result = new StringBuilder();
153 for (Iterator<? extends T> iterator = object.iterator(); iterator.hasNext(); ) {
154 T next = iterator.next();
155 result.append(itemRenderer.render(next));
156 if (iterator.hasNext()) {
157 result.append(", ");
158 }
159 }
160 return result.toString();
161 }
162 };
163 }
164
165 public static final Renderer<InferenceErrorData> TYPE_INFERENCE_CONFLICTING_SUBSTITUTIONS_RENDERER =
166 new Renderer<InferenceErrorData>() {
167 @NotNull
168 @Override
169 public String render(@NotNull InferenceErrorData inferenceErrorData) {
170 return renderConflictingSubstitutionsInferenceError(inferenceErrorData, TabledDescriptorRenderer.create()).toString();
171 }
172 };
173
174 public static final Renderer<InferenceErrorData> TYPE_INFERENCE_TYPE_CONSTRUCTOR_MISMATCH_RENDERER =
175 new Renderer<InferenceErrorData>() {
176 @NotNull
177 @Override
178 public String render(@NotNull InferenceErrorData inferenceErrorData) {
179 return renderTypeConstructorMismatchError(inferenceErrorData, TabledDescriptorRenderer.create()).toString();
180 }
181 };
182
183 public static final Renderer<InferenceErrorData> TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER_RENDERER =
184 new Renderer<InferenceErrorData>() {
185 @NotNull
186 @Override
187 public String render(@NotNull InferenceErrorData inferenceErrorData) {
188 return renderNoInformationForParameterError(inferenceErrorData, TabledDescriptorRenderer.create()).toString();
189 }
190 };
191
192 public static final Renderer<InferenceErrorData> TYPE_INFERENCE_UPPER_BOUND_VIOLATED_RENDERER =
193 new Renderer<InferenceErrorData>() {
194 @NotNull
195 @Override
196 public String render(@NotNull InferenceErrorData inferenceErrorData) {
197 return renderUpperBoundViolatedInferenceError(inferenceErrorData, TabledDescriptorRenderer.create()).toString();
198 }
199 };
200
201 public static TabledDescriptorRenderer renderConflictingSubstitutionsInferenceError(InferenceErrorData inferenceErrorData,
202 TabledDescriptorRenderer result) {
203 LOG.assertTrue(inferenceErrorData.constraintSystem.getStatus().hasConflictingConstraints(), renderDebugMessage(
204 "Conflicting substitutions inference error renderer is applied for incorrect status", inferenceErrorData));
205
206 Collection<CallableDescriptor> substitutedDescriptors = Lists.newArrayList();
207 Collection<TypeSubstitutor> substitutors = ConstraintsUtil.getSubstitutorsForConflictingParameters(
208 inferenceErrorData.constraintSystem);
209 for (TypeSubstitutor substitutor : substitutors) {
210 CallableDescriptor substitutedDescriptor = inferenceErrorData.descriptor.substitute(substitutor);
211 substitutedDescriptors.add(substitutedDescriptor);
212 }
213
214 TypeParameterDescriptor firstConflictingParameter = ConstraintsUtil.getFirstConflictingParameter(inferenceErrorData.constraintSystem);
215 if (firstConflictingParameter == null) {
216 LOG.error(renderDebugMessage("There is no conflicting parameter for 'conflicting constraints' error.", inferenceErrorData));
217 return result;
218 }
219
220 result.text(newText()
221 .normal("Cannot infer type parameter ")
222 .strong(firstConflictingParameter.getName())
223 .normal(" in "));
224 //String type = strong(firstConflictingParameter.getName());
225 TableRenderer table = newTable();
226 result.table(table);
227 table.descriptor(inferenceErrorData.descriptor)
228 .text("None of the following substitutions");
229
230 for (CallableDescriptor substitutedDescriptor : substitutedDescriptors) {
231 JetType receiverType = DescriptorUtils.getReceiverParameterType(substitutedDescriptor.getReceiverParameter());
232
233 final Collection<ConstraintPosition> errorPositions = Sets.newHashSet();
234 List<JetType> parameterTypes = Lists.newArrayList();
235 for (ValueParameterDescriptor valueParameterDescriptor : substitutedDescriptor.getValueParameters()) {
236 parameterTypes.add(valueParameterDescriptor.getType());
237 if (valueParameterDescriptor.getIndex() >= inferenceErrorData.valueArgumentsTypes.size()) continue;
238 JetType actualType = inferenceErrorData.valueArgumentsTypes.get(valueParameterDescriptor.getIndex());
239 if (!JetTypeChecker.DEFAULT.isSubtypeOf(actualType, valueParameterDescriptor.getType())) {
240 errorPositions.add(ConstraintPosition.getValueParameterPosition(valueParameterDescriptor.getIndex()));
241 }
242 }
243
244 if (receiverType != null && inferenceErrorData.receiverArgumentType != null &&
245 !JetTypeChecker.DEFAULT.isSubtypeOf(inferenceErrorData.receiverArgumentType, receiverType)) {
246 errorPositions.add(ConstraintPosition.RECEIVER_POSITION);
247 }
248
249 Predicate<ConstraintPosition> isErrorPosition = new Predicate<ConstraintPosition>() {
250 @Override
251 public boolean apply(@Nullable ConstraintPosition constraintPosition) {
252 return errorPositions.contains(constraintPosition);
253 }
254 };
255 table.functionArgumentTypeList(receiverType, parameterTypes, isErrorPosition);
256 }
257
258 table.text("can be applied to")
259 .functionArgumentTypeList(inferenceErrorData.receiverArgumentType, inferenceErrorData.valueArgumentsTypes);
260
261 return result;
262 }
263
264 public static TabledDescriptorRenderer renderTypeConstructorMismatchError(final InferenceErrorData inferenceErrorData,
265 TabledDescriptorRenderer renderer) {
266 Predicate<ConstraintPosition> isErrorPosition = new Predicate<ConstraintPosition>() {
267 @Override
268 public boolean apply(ConstraintPosition constraintPosition) {
269 return inferenceErrorData.constraintSystem.getStatus().hasTypeConstructorMismatchAt(constraintPosition);
270 }
271 };
272 return renderer.table(TabledDescriptorRenderer.newTable()
273 .descriptor(inferenceErrorData.descriptor)
274 .text("cannot be applied to")
275 .functionArgumentTypeList(
276 inferenceErrorData.receiverArgumentType,
277 inferenceErrorData.valueArgumentsTypes,
278 isErrorPosition));
279 }
280
281 public static TabledDescriptorRenderer renderNoInformationForParameterError(InferenceErrorData inferenceErrorData,
282 TabledDescriptorRenderer result) {
283 TypeParameterDescriptor firstUnknownParameter = null;
284 for (TypeParameterDescriptor typeParameter : inferenceErrorData.constraintSystem.getTypeVariables()) {
285 if (inferenceErrorData.constraintSystem.getTypeBounds(typeParameter).isEmpty()) {
286 firstUnknownParameter = typeParameter;
287 break;
288 }
289 }
290 if (firstUnknownParameter == null) {
291 LOG.error(renderDebugMessage("There is no unknown parameter for 'no information for parameter error'.", inferenceErrorData));
292 return result;
293 }
294
295 return result
296 .text(newText().normal("Not enough information to infer parameter ")
297 .strong(firstUnknownParameter.getName())
298 .normal(" in "))
299 .table(newTable()
300 .descriptor(inferenceErrorData.descriptor)
301 .text("Please specify it explicitly."));
302 }
303
304 @NotNull
305 public static TabledDescriptorRenderer renderUpperBoundViolatedInferenceError(InferenceErrorData inferenceErrorData, TabledDescriptorRenderer result) {
306 TypeParameterDescriptor typeParameterDescriptor = null;
307 ConstraintSystemImpl constraintSystem = (ConstraintSystemImpl) inferenceErrorData.constraintSystem;
308 ConstraintSystemStatus status = constraintSystem.getStatus();
309 LOG.assertTrue(status.hasViolatedUpperBound(), renderDebugMessage(
310 "Upper bound violated renderer is applied for incorrect status", inferenceErrorData));
311
312 ConstraintSystem systemWithoutWeakConstraints = constraintSystem.getSystemWithoutWeakConstraints();
313 for (TypeParameterDescriptor typeParameter : inferenceErrorData.descriptor.getTypeParameters()) {
314 if (!ConstraintsUtil.checkUpperBoundIsSatisfied(systemWithoutWeakConstraints, typeParameter, true)) {
315 typeParameterDescriptor = typeParameter;
316 }
317 }
318 if (typeParameterDescriptor == null && status.hasConflictingConstraints()) {
319 return renderConflictingSubstitutionsInferenceError(inferenceErrorData, result);
320 }
321 if (typeParameterDescriptor == null) {
322 LOG.error(renderDebugMessage("There is no type parameter with violated upper bound for 'upper bound violated' error",
323 inferenceErrorData));
324 return result;
325 }
326
327 JetType inferredValueForTypeParameter = systemWithoutWeakConstraints.getTypeBounds(typeParameterDescriptor).getValue();
328 if (inferredValueForTypeParameter == null) {
329 LOG.error(renderDebugMessage("System without weak constraints is not successful, there is no value for type parameter " +
330 typeParameterDescriptor.getName() + "\n: " + systemWithoutWeakConstraints, inferenceErrorData));
331 return result;
332 }
333
334 result.text(newText().normal("Type parameter bound for ").strong(typeParameterDescriptor.getName()).normal(" in "))
335 .table(newTable().
336 descriptor(inferenceErrorData.descriptor));
337
338 JetType violatedUpperBound = null;
339 for (JetType upperBound : typeParameterDescriptor.getUpperBounds()) {
340 JetType upperBoundWithSubstitutedInferredTypes =
341 systemWithoutWeakConstraints.getResultingSubstitutor().substitute(upperBound, Variance.INVARIANT);
342 if (upperBoundWithSubstitutedInferredTypes != null &&
343 !JetTypeChecker.DEFAULT.isSubtypeOf(inferredValueForTypeParameter, upperBoundWithSubstitutedInferredTypes)) {
344 violatedUpperBound = upperBoundWithSubstitutedInferredTypes;
345 break;
346 }
347 }
348 if (violatedUpperBound == null) {
349 LOG.error(renderDebugMessage("Type parameter (chosen as violating its upper bound)" + typeParameterDescriptor.getName() +
350 " violates no bounds after substitution", inferenceErrorData));
351 return result;
352 }
353
354 Renderer<JetType> typeRenderer = result.getTypeRenderer();
355 result.text(newText()
356 .normal(" is not satisfied: inferred type ")
357 .error(typeRenderer.render(inferredValueForTypeParameter))
358 .normal(" is not a subtype of ")
359 .strong(typeRenderer.render(violatedUpperBound)));
360 return result;
361 }
362
363 public static final Renderer<Collection<ClassDescriptor>> CLASSES_OR_SEPARATED = new Renderer<Collection<ClassDescriptor>>() {
364 @NotNull
365 @Override
366 public String render(@NotNull Collection<ClassDescriptor> descriptors) {
367 StringBuilder sb = new StringBuilder();
368 int index = 0;
369 for (ClassDescriptor descriptor : descriptors) {
370 sb.append(DescriptorUtils.getFqName(descriptor).asString());
371 index++;
372 if (index <= descriptors.size() - 2) {
373 sb.append(", ");
374 }
375 else if (index == descriptors.size() - 1) {
376 sb.append(" or ");
377 }
378 }
379 return sb.toString();
380 }
381 };
382
383 public static final Renderer<Collection<JetType>> RENDER_COLLECTION_OF_TYPES = new Renderer<Collection<JetType>>() {
384 @NotNull
385 @Override
386 public String render(@NotNull Collection<JetType> types) {
387 return StringUtil.join(types, new Function<JetType, String>() {
388 @Override
389 public String fun(JetType type) {
390 return RENDER_TYPE.render(type);
391 }
392 }, ", ");
393 }
394 };
395
396 public static final Renderer<ConstraintSystem> RENDER_CONSTRAINT_SYSTEM = new Renderer<ConstraintSystem>() {
397 @NotNull
398 @Override
399 public String render(@NotNull ConstraintSystem constraintSystem) {
400 Set<TypeParameterDescriptor> typeVariables = constraintSystem.getTypeVariables();
401 Set<TypeBounds> typeBounds = Sets.newLinkedHashSet();
402 for (TypeParameterDescriptor variable : typeVariables) {
403 typeBounds.add(constraintSystem.getTypeBounds(variable));
404 }
405 Function<TypeBounds, String> renderTypeBounds = rendererToFunction(RENDER_TYPE_BOUNDS);
406 return "type parameter bounds:\n" + StringUtil.join(typeBounds, renderTypeBounds, "\n") + "\n" +
407 "status:\n" + ConstraintsUtil.getDebugMessageForStatus(constraintSystem.getStatus());
408 }
409 };
410
411 public static final Renderer<TypeBounds> RENDER_TYPE_BOUNDS = new Renderer<TypeBounds>() {
412 @NotNull
413 @Override
414 public String render(@NotNull TypeBounds typeBounds) {
415 Function<TypeBoundsImpl.Bound, String> renderBound = new Function<TypeBoundsImpl.Bound, String>() {
416 @Override
417 public String fun(TypeBoundsImpl.Bound bound) {
418 String arrow = bound.kind == LOWER_BOUND ? ">: " : bound.kind == UPPER_BOUND ? "<: " : ":= ";
419 return arrow + RENDER_TYPE.render(bound.type) + '(' + bound.position + ')';
420 }
421 };
422 Name typeVariableName = typeBounds.getTypeVariable().getName();
423 if (typeBounds.isEmpty()) {
424 return typeVariableName.asString();
425 }
426 return typeVariableName + " " + StringUtil.join(typeBounds.getBounds(), renderBound, ", ");
427 }
428 };
429
430 @NotNull
431 public static <T> Function<T, String> rendererToFunction(final @NotNull Renderer<T> renderer) {
432 return new Function<T, String>() {
433 @Override
434 public String fun(T t) {
435 return renderer.render(t);
436 }
437 };
438 }
439
440 @NotNull
441 private static String renderDebugMessage(String message, InferenceErrorData inferenceErrorData) {
442 StringBuilder result = new StringBuilder();
443 result.append(message);
444 result.append("\nConstraint system: \n");
445 result.append(RENDER_CONSTRAINT_SYSTEM.render(inferenceErrorData.constraintSystem));
446 result.append("\nDescriptor:\n");
447 result.append(inferenceErrorData.descriptor);
448 result.append("\nExpected type:\n");
449 if (TypeUtils.noExpectedType(inferenceErrorData.expectedType)) {
450 result.append(inferenceErrorData.expectedType);
451 }
452 else {
453 result.append(RENDER_TYPE.render(inferenceErrorData.expectedType));
454 }
455 result.append("\nArgument types:\n");
456 if (inferenceErrorData.receiverArgumentType != null) {
457 result.append(RENDER_TYPE.render(inferenceErrorData.receiverArgumentType)).append(".");
458 }
459 result.append("(").append(StringUtil.join(inferenceErrorData.valueArgumentsTypes, new Function<JetType, String>() {
460 @Override
461 public String fun(JetType type) {
462 return RENDER_TYPE.render(type);
463 }
464 }, ", ")).append(")");
465 return result.toString();
466 }
467
468 private Renderers() {
469 }
470 }