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.util.text.StringUtil;
023 import com.intellij.psi.PsiElement;
024 import com.intellij.util.Function;
025 import org.jetbrains.annotations.NotNull;
026 import org.jetbrains.annotations.Nullable;
027 import org.jetbrains.jet.lang.descriptors.*;
028 import org.jetbrains.jet.lang.psi.JetClass;
029 import org.jetbrains.jet.lang.psi.JetClassOrObject;
030 import org.jetbrains.jet.lang.resolve.DescriptorUtils;
031 import org.jetbrains.jet.lang.resolve.calls.inference.*;
032 import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
033 import org.jetbrains.jet.lang.types.JetType;
034 import org.jetbrains.jet.lang.types.TypeSubstitutor;
035 import org.jetbrains.jet.lang.types.Variance;
036 import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
037 import org.jetbrains.jet.renderer.DescriptorRenderer;
038 import org.jetbrains.jet.renderer.Renderer;
039
040 import java.util.Collection;
041 import java.util.Iterator;
042 import java.util.List;
043
044 import static org.jetbrains.jet.lang.diagnostics.rendering.TabledDescriptorRenderer.*;
045
046 public class Renderers {
047 public static final Renderer<Object> TO_STRING = new Renderer<Object>() {
048 @NotNull
049 @Override
050 public String render(@NotNull Object element) {
051 return element.toString();
052 }
053
054 @Override
055 public String toString() {
056 return "TO_STRING";
057 }
058 };
059
060 public static final Renderer<Object> NAME = new Renderer<Object>() {
061 @NotNull
062 @Override
063 public String render(@NotNull Object element) {
064 if (element instanceof Named) {
065 return ((Named) element).getName().asString();
066 }
067 return element.toString();
068 }
069 };
070
071 public static final Renderer<PsiElement> ELEMENT_TEXT = new Renderer<PsiElement>() {
072 @NotNull
073 @Override
074 public String render(@NotNull PsiElement element) {
075 return element.getText();
076 }
077 };
078
079 public static final Renderer<JetClassOrObject> RENDER_CLASS_OR_OBJECT = new Renderer<JetClassOrObject>() {
080 @NotNull
081 @Override
082 public String render(@NotNull JetClassOrObject classOrObject) {
083 String name = classOrObject.getName() != null ? " '" + classOrObject.getName() + "'" : "";
084 if (classOrObject instanceof JetClass) {
085 return "Class" + name;
086 }
087 return "Object" + name;
088
089 }
090 };
091
092 public static final Renderer<JetType> RENDER_TYPE = new Renderer<JetType>() {
093 @NotNull
094 @Override
095 public String render(@NotNull JetType type) {
096 return DescriptorRenderer.TEXT.renderType(type);
097 }
098 };
099
100 public static final Renderer<Collection<? extends ResolvedCall<?>>> AMBIGUOUS_CALLS =
101 new Renderer<Collection<? extends ResolvedCall<? extends CallableDescriptor>>>() {
102 @NotNull
103 @Override
104 public String render(@NotNull Collection<? extends ResolvedCall<? extends CallableDescriptor>> argument) {
105 StringBuilder stringBuilder = new StringBuilder("\n");
106 for (ResolvedCall<? extends CallableDescriptor> call : argument) {
107 stringBuilder.append(DescriptorRenderer.TEXT.render(call.getResultingDescriptor())).append("\n");
108 }
109 return stringBuilder.toString();
110 }
111 };
112
113 public static <T> Renderer<Collection<? extends T>> commaSeparated(final Renderer<T> itemRenderer) {
114 return new Renderer<Collection<? extends T>>() {
115 @NotNull
116 @Override
117 public String render(@NotNull Collection<? extends T> object) {
118 StringBuilder result = new StringBuilder();
119 for (Iterator<? extends T> iterator = object.iterator(); iterator.hasNext(); ) {
120 T next = iterator.next();
121 result.append(itemRenderer.render(next));
122 if (iterator.hasNext()) {
123 result.append(", ");
124 }
125 }
126 return result.toString();
127 }
128 };
129 }
130
131 public static final Renderer<InferenceErrorData> TYPE_INFERENCE_CONFLICTING_SUBSTITUTIONS_RENDERER =
132 new Renderer<InferenceErrorData>() {
133 @NotNull
134 @Override
135 public String render(@NotNull InferenceErrorData inferenceErrorData) {
136 return renderConflictingSubstitutionsInferenceError(inferenceErrorData, TabledDescriptorRenderer.create()).toString();
137 }
138 };
139
140 public static final Renderer<InferenceErrorData> TYPE_INFERENCE_TYPE_CONSTRUCTOR_MISMATCH_RENDERER =
141 new Renderer<InferenceErrorData>() {
142 @NotNull
143 @Override
144 public String render(@NotNull InferenceErrorData inferenceErrorData) {
145 return renderTypeConstructorMismatchError(inferenceErrorData, TabledDescriptorRenderer.create()).toString();
146 }
147 };
148
149 public static final Renderer<InferenceErrorData> TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER_RENDERER =
150 new Renderer<InferenceErrorData>() {
151 @NotNull
152 @Override
153 public String render(@NotNull InferenceErrorData inferenceErrorData) {
154 return renderNoInformationForParameterError(inferenceErrorData, TabledDescriptorRenderer.create()).toString();
155 }
156 };
157
158 public static final Renderer<InferenceErrorData> TYPE_INFERENCE_UPPER_BOUND_VIOLATED_RENDERER =
159 new Renderer<InferenceErrorData>() {
160 @NotNull
161 @Override
162 public String render(@NotNull InferenceErrorData inferenceErrorData) {
163 return renderUpperBoundViolatedInferenceError(inferenceErrorData, TabledDescriptorRenderer.create()).toString();
164 }
165 };
166
167 public static TabledDescriptorRenderer renderConflictingSubstitutionsInferenceError(InferenceErrorData inferenceErrorData,
168 TabledDescriptorRenderer result) {
169 assert inferenceErrorData.constraintSystem.getStatus().hasConflictingConstraints();
170
171 Collection<CallableDescriptor> substitutedDescriptors = Lists.newArrayList();
172 Collection<TypeSubstitutor> substitutors = ConstraintsUtil.getSubstitutorsForConflictingParameters(
173 inferenceErrorData.constraintSystem);
174 for (TypeSubstitutor substitutor : substitutors) {
175 CallableDescriptor substitutedDescriptor = inferenceErrorData.descriptor.substitute(substitutor);
176 substitutedDescriptors.add(substitutedDescriptor);
177 }
178
179 TypeParameterDescriptor firstConflictingParameter = ConstraintsUtil.getFirstConflictingParameter(inferenceErrorData.constraintSystem);
180 assert firstConflictingParameter != null;
181
182 result.text(newText()
183 .normal("Cannot infer type parameter ")
184 .strong(firstConflictingParameter.getName())
185 .normal(" in "));
186 //String type = strong(firstConflictingParameter.getName());
187 TableRenderer table = newTable();
188 result.table(table);
189 table.descriptor(inferenceErrorData.descriptor)
190 .text("None of the following substitutions");
191
192 for (CallableDescriptor substitutedDescriptor : substitutedDescriptors) {
193 JetType receiverType = DescriptorUtils.getReceiverParameterType(substitutedDescriptor.getReceiverParameter());
194
195 final Collection<ConstraintPosition> errorPositions = Sets.newHashSet();
196 List<JetType> parameterTypes = Lists.newArrayList();
197 for (ValueParameterDescriptor valueParameterDescriptor : substitutedDescriptor.getValueParameters()) {
198 parameterTypes.add(valueParameterDescriptor.getType());
199 if (valueParameterDescriptor.getIndex() >= inferenceErrorData.valueArgumentsTypes.size()) continue;
200 JetType actualType = inferenceErrorData.valueArgumentsTypes.get(valueParameterDescriptor.getIndex());
201 if (!JetTypeChecker.INSTANCE.isSubtypeOf(actualType, valueParameterDescriptor.getType())) {
202 errorPositions.add(ConstraintPosition.getValueParameterPosition(valueParameterDescriptor.getIndex()));
203 }
204 }
205
206 if (receiverType != null && inferenceErrorData.receiverArgumentType != null &&
207 !JetTypeChecker.INSTANCE.isSubtypeOf(inferenceErrorData.receiverArgumentType, receiverType)) {
208 errorPositions.add(ConstraintPosition.RECEIVER_POSITION);
209 }
210
211 Predicate<ConstraintPosition> isErrorPosition = new Predicate<ConstraintPosition>() {
212 @Override
213 public boolean apply(@Nullable ConstraintPosition constraintPosition) {
214 return errorPositions.contains(constraintPosition);
215 }
216 };
217 table.functionArgumentTypeList(receiverType, parameterTypes, isErrorPosition);
218 }
219
220 table.text("can be applied to")
221 .functionArgumentTypeList(inferenceErrorData.receiverArgumentType, inferenceErrorData.valueArgumentsTypes);
222
223 return result;
224 }
225
226 public static TabledDescriptorRenderer renderTypeConstructorMismatchError(final InferenceErrorData inferenceErrorData,
227 TabledDescriptorRenderer renderer) {
228 Predicate<ConstraintPosition> isErrorPosition = new Predicate<ConstraintPosition>() {
229 @Override
230 public boolean apply(@Nullable ConstraintPosition constraintPosition) {
231 assert constraintPosition != null;
232 return inferenceErrorData.constraintSystem.getStatus().hasTypeConstructorMismatchAt(constraintPosition);
233 }
234 };
235 return renderer.table(TabledDescriptorRenderer.newTable()
236 .descriptor(inferenceErrorData.descriptor)
237 .text("cannot be applied to")
238 .functionArgumentTypeList(
239 inferenceErrorData.receiverArgumentType,
240 inferenceErrorData.valueArgumentsTypes,
241 isErrorPosition));
242 }
243
244 public static TabledDescriptorRenderer renderNoInformationForParameterError(InferenceErrorData inferenceErrorData,
245 TabledDescriptorRenderer renderer) {
246 TypeParameterDescriptor firstUnknownParameter = null;
247 for (TypeParameterDescriptor typeParameter : inferenceErrorData.constraintSystem.getTypeVariables()) {
248 if (inferenceErrorData.constraintSystem.getTypeBounds(typeParameter).isEmpty()) {
249 firstUnknownParameter = typeParameter;
250 break;
251 }
252 }
253 assert firstUnknownParameter != null;
254
255 return renderer
256 .text(newText().normal("Not enough information to infer parameter ")
257 .strong(firstUnknownParameter.getName())
258 .normal(" in "))
259 .table(newTable()
260 .descriptor(inferenceErrorData.descriptor)
261 .text("Please specify it explicitly."));
262 }
263
264 @NotNull
265 public static TabledDescriptorRenderer renderUpperBoundViolatedInferenceError(InferenceErrorData inferenceErrorData, TabledDescriptorRenderer result) {
266 String errorMessage = "Rendering 'upper bound violated' error for " + inferenceErrorData.descriptor;
267
268 TypeParameterDescriptor typeParameterDescriptor = null;
269 ConstraintSystemImpl constraintSystem = (ConstraintSystemImpl) inferenceErrorData.constraintSystem;
270 assert constraintSystem.getStatus().hasViolatedUpperBound();
271
272 ConstraintSystem systemWithoutWeakConstraints = constraintSystem.getSystemWithoutWeakConstraints();
273 for (TypeParameterDescriptor typeParameter : inferenceErrorData.descriptor.getTypeParameters()) {
274 if (!ConstraintsUtil.checkUpperBoundIsSatisfied(systemWithoutWeakConstraints, typeParameter, true)) {
275 typeParameterDescriptor = typeParameter;
276 }
277 }
278 assert typeParameterDescriptor != null : errorMessage;
279
280 JetType inferredValueForTypeParameter = systemWithoutWeakConstraints.getTypeBounds(typeParameterDescriptor).getValue();
281 assert inferredValueForTypeParameter != null : errorMessage;
282
283 result.text(newText().normal("Type parameter bound for ").strong(typeParameterDescriptor.getName()).normal(" in "))
284 .table(newTable().
285 descriptor(inferenceErrorData.descriptor));
286
287 JetType violatedUpperBound = null;
288 for (JetType upperBound : typeParameterDescriptor.getUpperBounds()) {
289 JetType upperBoundWithSubstitutedInferredTypes =
290 systemWithoutWeakConstraints.getResultingSubstitutor().substitute(upperBound, Variance.INVARIANT);
291 if (upperBoundWithSubstitutedInferredTypes != null &&
292 !JetTypeChecker.INSTANCE.isSubtypeOf(inferredValueForTypeParameter, upperBoundWithSubstitutedInferredTypes)) {
293 violatedUpperBound = upperBoundWithSubstitutedInferredTypes;
294 break;
295 }
296 }
297 assert violatedUpperBound != null : errorMessage;
298
299 Renderer<JetType> typeRenderer = result.getTypeRenderer();
300 result.text(newText()
301 .normal(" is not satisfied: inferred type ")
302 .error(typeRenderer.render(inferredValueForTypeParameter))
303 .normal(" is not a subtype of ")
304 .strong(typeRenderer.render(violatedUpperBound)));
305 return result;
306 }
307
308 public static final Renderer<Collection<ClassDescriptor>> CLASSES_OR_SEPARATED = new Renderer<Collection<ClassDescriptor>>() {
309 @NotNull
310 @Override
311 public String render(@NotNull Collection<ClassDescriptor> descriptors) {
312 StringBuilder sb = new StringBuilder();
313 int index = 0;
314 for (ClassDescriptor descriptor : descriptors) {
315 sb.append(DescriptorUtils.getFQName(descriptor).asString());
316 index++;
317 if (index <= descriptors.size() - 2) {
318 sb.append(", ");
319 }
320 else if (index == descriptors.size() - 1) {
321 sb.append(" or ");
322 }
323 }
324 return sb.toString();
325 }
326 };
327
328 public static final Renderer<Collection<JetType>> RENDER_COLLECTION_OF_TYPES = new Renderer<Collection<JetType>>() {
329 @NotNull
330 @Override
331 public String render(@NotNull Collection<JetType> types) {
332 return StringUtil.join(types, new Function<JetType, String>() {
333 @Override
334 public String fun(JetType type) {
335 return RENDER_TYPE.render(type);
336 }
337 }, ", ");
338 }
339 };
340
341
342 private Renderers() {
343 }
344 }