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
119 public static final Renderer<JetType> RENDER_TYPE = new Renderer<JetType>() {
120 @NotNull
121 @Override
122 public String render(@NotNull JetType type) {
123 return DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(type);
124 }
125 };
126
127 public static final Renderer<Collection<? extends ResolvedCall<?>>> AMBIGUOUS_CALLS =
128 new Renderer<Collection<? extends ResolvedCall<?>>>() {
129 @NotNull
130 @Override
131 public String render(@NotNull Collection<? extends ResolvedCall<?>> argument) {
132 StringBuilder stringBuilder = new StringBuilder("\n");
133 for (ResolvedCall<?> call : argument) {
134 stringBuilder.append(DescriptorRenderer.FQ_NAMES_IN_TYPES.render(call.getResultingDescriptor())).append("\n");
135 }
136 return stringBuilder.toString();
137 }
138 };
139
140 public static <T> Renderer<Collection<? extends T>> commaSeparated(final Renderer<T> itemRenderer) {
141 return new Renderer<Collection<? extends T>>() {
142 @NotNull
143 @Override
144 public String render(@NotNull Collection<? extends T> object) {
145 StringBuilder result = new StringBuilder();
146 for (Iterator<? extends T> iterator = object.iterator(); iterator.hasNext(); ) {
147 T next = iterator.next();
148 result.append(itemRenderer.render(next));
149 if (iterator.hasNext()) {
150 result.append(", ");
151 }
152 }
153 return result.toString();
154 }
155 };
156 }
157
158 public static final Renderer<InferenceErrorData> TYPE_INFERENCE_CONFLICTING_SUBSTITUTIONS_RENDERER =
159 new Renderer<InferenceErrorData>() {
160 @NotNull
161 @Override
162 public String render(@NotNull InferenceErrorData inferenceErrorData) {
163 return renderConflictingSubstitutionsInferenceError(inferenceErrorData, TabledDescriptorRenderer.create()).toString();
164 }
165 };
166
167 public static final Renderer<InferenceErrorData> TYPE_INFERENCE_TYPE_CONSTRUCTOR_MISMATCH_RENDERER =
168 new Renderer<InferenceErrorData>() {
169 @NotNull
170 @Override
171 public String render(@NotNull InferenceErrorData inferenceErrorData) {
172 return renderTypeConstructorMismatchError(inferenceErrorData, TabledDescriptorRenderer.create()).toString();
173 }
174 };
175
176 public static final Renderer<InferenceErrorData> TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER_RENDERER =
177 new Renderer<InferenceErrorData>() {
178 @NotNull
179 @Override
180 public String render(@NotNull InferenceErrorData inferenceErrorData) {
181 return renderNoInformationForParameterError(inferenceErrorData, TabledDescriptorRenderer.create()).toString();
182 }
183 };
184
185 public static final Renderer<InferenceErrorData> TYPE_INFERENCE_UPPER_BOUND_VIOLATED_RENDERER =
186 new Renderer<InferenceErrorData>() {
187 @NotNull
188 @Override
189 public String render(@NotNull InferenceErrorData inferenceErrorData) {
190 return renderUpperBoundViolatedInferenceError(inferenceErrorData, TabledDescriptorRenderer.create()).toString();
191 }
192 };
193
194 public static TabledDescriptorRenderer renderConflictingSubstitutionsInferenceError(InferenceErrorData inferenceErrorData,
195 TabledDescriptorRenderer result) {
196 LOG.assertTrue(inferenceErrorData.constraintSystem.getStatus().hasConflictingConstraints(), renderDebugMessage(
197 "Conflicting substitutions inference error renderer is applied for incorrect status", inferenceErrorData));
198
199 Collection<CallableDescriptor> substitutedDescriptors = Lists.newArrayList();
200 Collection<TypeSubstitutor> substitutors = ConstraintsUtil.getSubstitutorsForConflictingParameters(
201 inferenceErrorData.constraintSystem);
202 for (TypeSubstitutor substitutor : substitutors) {
203 CallableDescriptor substitutedDescriptor = inferenceErrorData.descriptor.substitute(substitutor);
204 substitutedDescriptors.add(substitutedDescriptor);
205 }
206
207 TypeParameterDescriptor firstConflictingParameter = ConstraintsUtil.getFirstConflictingParameter(inferenceErrorData.constraintSystem);
208 if (firstConflictingParameter == null) {
209 LOG.error(renderDebugMessage("There is no conflicting parameter for 'conflicting constraints' error.", inferenceErrorData));
210 return result;
211 }
212
213 result.text(newText()
214 .normal("Cannot infer type parameter ")
215 .strong(firstConflictingParameter.getName())
216 .normal(" in "));
217 //String type = strong(firstConflictingParameter.getName());
218 TableRenderer table = newTable();
219 result.table(table);
220 table.descriptor(inferenceErrorData.descriptor)
221 .text("None of the following substitutions");
222
223 for (CallableDescriptor substitutedDescriptor : substitutedDescriptors) {
224 JetType receiverType = DescriptorUtils.getReceiverParameterType(substitutedDescriptor.getReceiverParameter());
225
226 final Collection<ConstraintPosition> errorPositions = Sets.newHashSet();
227 List<JetType> parameterTypes = Lists.newArrayList();
228 for (ValueParameterDescriptor valueParameterDescriptor : substitutedDescriptor.getValueParameters()) {
229 parameterTypes.add(valueParameterDescriptor.getType());
230 if (valueParameterDescriptor.getIndex() >= inferenceErrorData.valueArgumentsTypes.size()) continue;
231 JetType actualType = inferenceErrorData.valueArgumentsTypes.get(valueParameterDescriptor.getIndex());
232 if (!JetTypeChecker.INSTANCE.isSubtypeOf(actualType, valueParameterDescriptor.getType())) {
233 errorPositions.add(ConstraintPosition.getValueParameterPosition(valueParameterDescriptor.getIndex()));
234 }
235 }
236
237 if (receiverType != null && inferenceErrorData.receiverArgumentType != null &&
238 !JetTypeChecker.INSTANCE.isSubtypeOf(inferenceErrorData.receiverArgumentType, receiverType)) {
239 errorPositions.add(ConstraintPosition.RECEIVER_POSITION);
240 }
241
242 Predicate<ConstraintPosition> isErrorPosition = new Predicate<ConstraintPosition>() {
243 @Override
244 public boolean apply(@Nullable ConstraintPosition constraintPosition) {
245 return errorPositions.contains(constraintPosition);
246 }
247 };
248 table.functionArgumentTypeList(receiverType, parameterTypes, isErrorPosition);
249 }
250
251 table.text("can be applied to")
252 .functionArgumentTypeList(inferenceErrorData.receiverArgumentType, inferenceErrorData.valueArgumentsTypes);
253
254 return result;
255 }
256
257 public static TabledDescriptorRenderer renderTypeConstructorMismatchError(final InferenceErrorData inferenceErrorData,
258 TabledDescriptorRenderer renderer) {
259 Predicate<ConstraintPosition> isErrorPosition = new Predicate<ConstraintPosition>() {
260 @Override
261 public boolean apply(ConstraintPosition constraintPosition) {
262 return inferenceErrorData.constraintSystem.getStatus().hasTypeConstructorMismatchAt(constraintPosition);
263 }
264 };
265 return renderer.table(TabledDescriptorRenderer.newTable()
266 .descriptor(inferenceErrorData.descriptor)
267 .text("cannot be applied to")
268 .functionArgumentTypeList(
269 inferenceErrorData.receiverArgumentType,
270 inferenceErrorData.valueArgumentsTypes,
271 isErrorPosition));
272 }
273
274 public static TabledDescriptorRenderer renderNoInformationForParameterError(InferenceErrorData inferenceErrorData,
275 TabledDescriptorRenderer result) {
276 TypeParameterDescriptor firstUnknownParameter = null;
277 for (TypeParameterDescriptor typeParameter : inferenceErrorData.constraintSystem.getTypeVariables()) {
278 if (inferenceErrorData.constraintSystem.getTypeBounds(typeParameter).isEmpty()) {
279 firstUnknownParameter = typeParameter;
280 break;
281 }
282 }
283 if (firstUnknownParameter == null) {
284 LOG.error(renderDebugMessage("There is no unknown parameter for 'no information for parameter error'.", inferenceErrorData));
285 return result;
286 }
287
288 return result
289 .text(newText().normal("Not enough information to infer parameter ")
290 .strong(firstUnknownParameter.getName())
291 .normal(" in "))
292 .table(newTable()
293 .descriptor(inferenceErrorData.descriptor)
294 .text("Please specify it explicitly."));
295 }
296
297 @NotNull
298 public static TabledDescriptorRenderer renderUpperBoundViolatedInferenceError(InferenceErrorData inferenceErrorData, TabledDescriptorRenderer result) {
299 TypeParameterDescriptor typeParameterDescriptor = null;
300 ConstraintSystemImpl constraintSystem = (ConstraintSystemImpl) inferenceErrorData.constraintSystem;
301 ConstraintSystemStatus status = constraintSystem.getStatus();
302 LOG.assertTrue(status.hasViolatedUpperBound(), renderDebugMessage(
303 "Upper bound violated renderer is applied for incorrect status", inferenceErrorData));
304
305 ConstraintSystem systemWithoutWeakConstraints = constraintSystem.getSystemWithoutWeakConstraints();
306 for (TypeParameterDescriptor typeParameter : inferenceErrorData.descriptor.getTypeParameters()) {
307 if (!ConstraintsUtil.checkUpperBoundIsSatisfied(systemWithoutWeakConstraints, typeParameter, true)) {
308 typeParameterDescriptor = typeParameter;
309 }
310 }
311 if (typeParameterDescriptor == null && status.hasConflictingConstraints()) {
312 return renderConflictingSubstitutionsInferenceError(inferenceErrorData, result);
313 }
314 if (typeParameterDescriptor == null) {
315 LOG.error(renderDebugMessage("There is no type parameter with violated upper bound for 'upper bound violated' error",
316 inferenceErrorData));
317 return result;
318 }
319
320 JetType inferredValueForTypeParameter = systemWithoutWeakConstraints.getTypeBounds(typeParameterDescriptor).getValue();
321 if (inferredValueForTypeParameter == null) {
322 LOG.error(renderDebugMessage("System without weak constraints is not successful, there is no value for type parameter " +
323 typeParameterDescriptor.getName() + "\n: " + systemWithoutWeakConstraints, inferenceErrorData));
324 return result;
325 }
326
327 result.text(newText().normal("Type parameter bound for ").strong(typeParameterDescriptor.getName()).normal(" in "))
328 .table(newTable().
329 descriptor(inferenceErrorData.descriptor));
330
331 JetType violatedUpperBound = null;
332 for (JetType upperBound : typeParameterDescriptor.getUpperBounds()) {
333 JetType upperBoundWithSubstitutedInferredTypes =
334 systemWithoutWeakConstraints.getResultingSubstitutor().substitute(upperBound, Variance.INVARIANT);
335 if (upperBoundWithSubstitutedInferredTypes != null &&
336 !JetTypeChecker.INSTANCE.isSubtypeOf(inferredValueForTypeParameter, upperBoundWithSubstitutedInferredTypes)) {
337 violatedUpperBound = upperBoundWithSubstitutedInferredTypes;
338 break;
339 }
340 }
341 if (violatedUpperBound == null) {
342 LOG.error(renderDebugMessage("Type parameter (chosen as violating its upper bound)" + typeParameterDescriptor.getName() +
343 " violates no bounds after substitution", inferenceErrorData));
344 return result;
345 }
346
347 Renderer<JetType> typeRenderer = result.getTypeRenderer();
348 result.text(newText()
349 .normal(" is not satisfied: inferred type ")
350 .error(typeRenderer.render(inferredValueForTypeParameter))
351 .normal(" is not a subtype of ")
352 .strong(typeRenderer.render(violatedUpperBound)));
353 return result;
354 }
355
356 public static final Renderer<Collection<ClassDescriptor>> CLASSES_OR_SEPARATED = new Renderer<Collection<ClassDescriptor>>() {
357 @NotNull
358 @Override
359 public String render(@NotNull Collection<ClassDescriptor> descriptors) {
360 StringBuilder sb = new StringBuilder();
361 int index = 0;
362 for (ClassDescriptor descriptor : descriptors) {
363 sb.append(DescriptorUtils.getFqName(descriptor).asString());
364 index++;
365 if (index <= descriptors.size() - 2) {
366 sb.append(", ");
367 }
368 else if (index == descriptors.size() - 1) {
369 sb.append(" or ");
370 }
371 }
372 return sb.toString();
373 }
374 };
375
376 public static final Renderer<Collection<JetType>> RENDER_COLLECTION_OF_TYPES = new Renderer<Collection<JetType>>() {
377 @NotNull
378 @Override
379 public String render(@NotNull Collection<JetType> types) {
380 return StringUtil.join(types, new Function<JetType, String>() {
381 @Override
382 public String fun(JetType type) {
383 return RENDER_TYPE.render(type);
384 }
385 }, ", ");
386 }
387 };
388
389 public static final Renderer<ConstraintSystem> RENDER_CONSTRAINT_SYSTEM = new Renderer<ConstraintSystem>() {
390 @NotNull
391 @Override
392 public String render(@NotNull ConstraintSystem constraintSystem) {
393 Set<TypeParameterDescriptor> typeVariables = constraintSystem.getTypeVariables();
394 Set<TypeBounds> typeBounds = Sets.newLinkedHashSet();
395 for (TypeParameterDescriptor variable : typeVariables) {
396 typeBounds.add(constraintSystem.getTypeBounds(variable));
397 }
398 Function<TypeBounds, String> renderTypeBounds = rendererToFunction(RENDER_TYPE_BOUNDS);
399 return "type parameter bounds:\n" + StringUtil.join(typeBounds, renderTypeBounds, "\n") + "\n" +
400 "status:\n" + ConstraintsUtil.getDebugMessageForStatus(constraintSystem.getStatus());
401 }
402 };
403
404 public static final Renderer<TypeBounds> RENDER_TYPE_BOUNDS = new Renderer<TypeBounds>() {
405 @NotNull
406 @Override
407 public String render(@NotNull TypeBounds typeBounds) {
408 Function<TypeBoundsImpl.Bound, String> renderBound = new Function<TypeBoundsImpl.Bound, String>() {
409 @Override
410 public String fun(TypeBoundsImpl.Bound bound) {
411 String arrow = bound.kind == LOWER_BOUND ? ">: " : bound.kind == UPPER_BOUND ? "<: " : ":= ";
412 return arrow + RENDER_TYPE.render(bound.type) + '(' + bound.position + ')';
413 }
414 };
415 Name typeVariableName = typeBounds.getTypeVariable().getName();
416 if (typeBounds.isEmpty()) {
417 return typeVariableName.asString();
418 }
419 return typeVariableName + " " + StringUtil.join(typeBounds.getBounds(), renderBound, ", ");
420 }
421 };
422
423 @NotNull
424 public static <T> Function<T, String> rendererToFunction(final @NotNull Renderer<T> renderer) {
425 return new Function<T, String>() {
426 @Override
427 public String fun(T t) {
428 return renderer.render(t);
429 }
430 };
431 }
432
433 @NotNull
434 private static String renderDebugMessage(String message, InferenceErrorData inferenceErrorData) {
435 StringBuilder result = new StringBuilder();
436 result.append(message);
437 result.append("\nConstraint system: \n");
438 result.append(RENDER_CONSTRAINT_SYSTEM.render(inferenceErrorData.constraintSystem));
439 result.append("\nDescriptor:\n");
440 result.append(inferenceErrorData.descriptor);
441 result.append("\nExpected type:\n");
442 if (TypeUtils.noExpectedType(inferenceErrorData.expectedType)) {
443 result.append(inferenceErrorData.expectedType);
444 }
445 else {
446 result.append(RENDER_TYPE.render(inferenceErrorData.expectedType));
447 }
448 result.append("\nArgument types:\n");
449 if (inferenceErrorData.receiverArgumentType != null) {
450 result.append(RENDER_TYPE.render(inferenceErrorData.receiverArgumentType)).append(".");
451 }
452 result.append("(").append(StringUtil.join(inferenceErrorData.valueArgumentsTypes, new Function<JetType, String>() {
453 @Override
454 public String fun(JetType type) {
455 return RENDER_TYPE.render(type);
456 }
457 }, ", ")).append(")");
458 return result.toString();
459 }
460
461 private Renderers() {
462 }
463 }