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;
018
019
020 import com.google.common.collect.ImmutableList;
021 import com.google.common.collect.Lists;
022 import com.intellij.lang.ASTNode;
023 import com.intellij.openapi.util.TextRange;
024 import com.intellij.psi.PsiElement;
025 import com.intellij.psi.PsiNameIdentifierOwner;
026 import kotlin.Function1;
027 import org.jetbrains.annotations.NotNull;
028 import org.jetbrains.jet.JetNodeTypes;
029 import org.jetbrains.jet.lang.psi.*;
030 import org.jetbrains.jet.lexer.JetKeywordToken;
031 import org.jetbrains.jet.lexer.JetModifierKeywordToken;
032 import org.jetbrains.jet.lexer.JetTokens;
033
034 import java.util.Arrays;
035 import java.util.Collections;
036 import java.util.List;
037
038 public class PositioningStrategies {
039
040 public static final PositioningStrategy<PsiElement> DEFAULT = new PositioningStrategy<PsiElement>() {
041 @NotNull
042 @Override
043 public List<TextRange> mark(@NotNull PsiElement element) {
044 if (element instanceof JetObjectLiteralExpression) {
045 JetObjectDeclaration objectDeclaration = ((JetObjectLiteralExpression) element).getObjectDeclaration();
046 PsiElement objectKeyword = objectDeclaration.getObjectKeyword();
047 JetDelegationSpecifierList delegationSpecifierList = objectDeclaration.getDelegationSpecifierList();
048 if (delegationSpecifierList == null) {
049 return markElement(objectKeyword);
050 }
051 return markRange(objectKeyword.getTextRange().union(delegationSpecifierList.getTextRange()));
052 }
053 return super.mark(element);
054 }
055 };
056
057 public static final PositioningStrategy<JetDeclaration> DECLARATION_RETURN_TYPE = new PositioningStrategy<JetDeclaration>() {
058 @NotNull
059 @Override
060 public List<TextRange> mark(@NotNull JetDeclaration declaration) {
061 return markElement(getElementToMark(declaration));
062 }
063
064 @Override
065 public boolean isValid(@NotNull JetDeclaration declaration) {
066 return !hasSyntaxErrors(getElementToMark(declaration));
067 }
068
069 private PsiElement getElementToMark(@NotNull JetDeclaration declaration) {
070 JetTypeReference returnTypeRef = null;
071 PsiElement nameIdentifierOrPlaceholder = null;
072 if (declaration instanceof JetNamedFunction) {
073 JetFunction function = (JetNamedFunction) declaration;
074 returnTypeRef = function.getReturnTypeRef();
075 nameIdentifierOrPlaceholder = function.getNameIdentifier();
076 }
077 else if (declaration instanceof JetProperty) {
078 JetProperty property = (JetProperty) declaration;
079 returnTypeRef = property.getTypeRef();
080 nameIdentifierOrPlaceholder = property.getNameIdentifier();
081 }
082 else if (declaration instanceof JetPropertyAccessor) {
083 JetPropertyAccessor accessor = (JetPropertyAccessor) declaration;
084 returnTypeRef = accessor.getReturnTypeReference();
085 nameIdentifierOrPlaceholder = accessor.getNamePlaceholder();
086 }
087
088 if (returnTypeRef != null) return returnTypeRef;
089 if (nameIdentifierOrPlaceholder != null) return nameIdentifierOrPlaceholder;
090 return declaration;
091 }
092 };
093
094 public static final PositioningStrategy<PsiNameIdentifierOwner> NAME_IDENTIFIER = new PositioningStrategy<PsiNameIdentifierOwner>() {
095 @NotNull
096 @Override
097 public List<TextRange> mark(@NotNull PsiNameIdentifierOwner element) {
098 PsiElement nameIdentifier = element.getNameIdentifier();
099 if (nameIdentifier != null) {
100 return markElement(nameIdentifier);
101 }
102 if (element instanceof JetObjectDeclaration) {
103 PsiElement objectKeyword = ((JetObjectDeclaration) element).getObjectKeyword();
104 PsiElement parent = element.getParent();
105 if (parent instanceof JetClassObject) {
106 PsiElement classKeyword = ((JetClassObject) parent).getClassKeywordNode();
107 PsiElement start = classKeyword == null ? objectKeyword : classKeyword;
108 return markRange(new TextRange(start.getTextRange().getStartOffset(), objectKeyword.getTextRange().getEndOffset()));
109 }
110 return markElement(objectKeyword);
111 }
112 return markElement(element);
113 }
114 };
115
116 public static final PositioningStrategy<PsiNameIdentifierOwner> NAMED_ELEMENT = new DeclarationHeader<PsiNameIdentifierOwner>() {
117 @Override
118 public boolean isValid(@NotNull PsiNameIdentifierOwner element) {
119 return (element.getNameIdentifier() != null || element instanceof JetObjectDeclaration) && super.isValid(element);
120 }
121 };
122
123 private static class DeclarationHeader<T extends PsiElement> extends PositioningStrategy<T> {
124 @NotNull
125 @Override
126 public List<TextRange> mark(@NotNull T element) {
127 if (element instanceof JetNamedFunction) {
128 JetNamedFunction function = (JetNamedFunction)element;
129 PsiElement endOfSignatureElement;
130 JetParameterList valueParameterList = function.getValueParameterList();
131 JetElement returnTypeRef = function.getReturnTypeRef();
132 PsiElement nameIdentifier = function.getNameIdentifier();
133 if (returnTypeRef != null) {
134 endOfSignatureElement = returnTypeRef;
135 }
136 else if (valueParameterList != null) {
137 endOfSignatureElement = valueParameterList;
138 }
139 else if (nameIdentifier != null) {
140 endOfSignatureElement = nameIdentifier;
141 }
142 else {
143 endOfSignatureElement = function;
144 }
145 return markRange(new TextRange(
146 function.getTextRange().getStartOffset(), endOfSignatureElement.getTextRange().getEndOffset()));
147 }
148 else if (element instanceof JetProperty) {
149 JetProperty property = (JetProperty) element;
150 PsiElement endOfSignatureElement;
151 JetTypeReference propertyTypeRef = property.getTypeRef();
152 PsiElement nameIdentifier = property.getNameIdentifier();
153 if (propertyTypeRef != null) {
154 endOfSignatureElement = propertyTypeRef;
155 }
156 else if (nameIdentifier != null) {
157 endOfSignatureElement = nameIdentifier;
158 }
159 else {
160 endOfSignatureElement = property;
161 }
162 return markRange(new TextRange(
163 property.getTextRange().getStartOffset(), endOfSignatureElement.getTextRange().getEndOffset()));
164 }
165 else if (element instanceof JetPropertyAccessor) {
166 JetPropertyAccessor accessor = (JetPropertyAccessor) element;
167 PsiElement endOfSignatureElement = accessor.getReturnTypeReference();
168 if (endOfSignatureElement == null) {
169 ASTNode rpar = accessor.getRightParenthesis();
170 endOfSignatureElement = rpar == null ? null : rpar.getPsi();
171 }
172 if (endOfSignatureElement == null) {
173 endOfSignatureElement = accessor.getNamePlaceholder();
174 }
175 return markRange(new TextRange(
176 accessor.getTextRange().getStartOffset(), endOfSignatureElement.getTextRange().getEndOffset()));
177 }
178 else if (element instanceof JetClass) {
179 PsiElement nameAsDeclaration = ((JetClass) element).getNameIdentifier();
180 if (nameAsDeclaration == null) {
181 return markElement(element);
182 }
183 PsiElement primaryConstructorParameterList = ((JetClass) element).getPrimaryConstructorParameterList();
184 if (primaryConstructorParameterList == null) {
185 return markRange(nameAsDeclaration.getTextRange());
186 }
187 return markRange(new TextRange(
188 nameAsDeclaration.getTextRange().getStartOffset(), primaryConstructorParameterList.getTextRange().getEndOffset()));
189 }
190 else if (element instanceof JetObjectDeclaration) {
191 return NAME_IDENTIFIER.mark((JetObjectDeclaration) element);
192 }
193 return super.mark(element);
194 }
195 }
196
197 public static final PositioningStrategy<JetDeclaration> DECLARATION = new PositioningStrategy<JetDeclaration>() {
198 @NotNull
199 @Override
200 public List<TextRange> mark(@NotNull JetDeclaration element) {
201 if (element instanceof PsiNameIdentifierOwner) {
202 return NAMED_ELEMENT.mark((PsiNameIdentifierOwner) element);
203 }
204 return super.mark(element);
205 }
206
207 @Override
208 public boolean isValid(@NotNull JetDeclaration element) {
209 if (element instanceof PsiNameIdentifierOwner) {
210 return NAMED_ELEMENT.isValid((PsiNameIdentifierOwner) element);
211 }
212 return super.isValid(element);
213 }
214 };
215
216 public static final PositioningStrategy<PsiElement> DECLARATION_OR_DEFAULT = new DeclarationHeader<PsiElement>() {
217 @NotNull
218 @Override
219 public List<TextRange> mark(@NotNull PsiElement element) {
220 if (element instanceof JetDeclaration) {
221 return super.mark((JetDeclaration) element);
222 }
223 return DEFAULT.mark(element);
224 }
225
226 @Override
227 public boolean isValid(@NotNull PsiElement element) {
228 if (element instanceof JetDeclaration) {
229 return DECLARATION.isValid((JetDeclaration) element);
230 }
231 return DEFAULT.isValid(element);
232 }
233 };
234
235 public static final PositioningStrategy<JetModifierListOwner> ABSTRACT_MODIFIER = modifierSetPosition(JetTokens.ABSTRACT_KEYWORD);
236
237 public static final PositioningStrategy<JetModifierListOwner> OVERRIDE_MODIFIER = modifierSetPosition(JetTokens.OVERRIDE_KEYWORD);
238
239 public static final PositioningStrategy<JetModifierListOwner> FINAL_MODIFIER = modifierSetPosition(JetTokens.FINAL_KEYWORD);
240
241 public static final PositioningStrategy<JetModifierListOwner> VARIANCE_MODIFIER = modifierSetPosition(JetTokens.IN_KEYWORD,
242 JetTokens.OUT_KEYWORD);
243 public static final PositioningStrategy<PsiElement> FOR_REDECLARATION = new PositioningStrategy<PsiElement>() {
244 @NotNull
245 @Override
246 public List<TextRange> mark(@NotNull PsiElement element) {
247 if (element instanceof JetNamedDeclaration) {
248 PsiElement nameIdentifier = ((JetNamedDeclaration) element).getNameIdentifier();
249 if (nameIdentifier != null) {
250 return markElement(nameIdentifier);
251 }
252 }
253 else if (element instanceof JetFile) {
254 JetFile file = (JetFile) element;
255 PsiElement nameIdentifier = file.getPackageDirective().getNameIdentifier();
256 if (nameIdentifier != null) {
257 return markElement(nameIdentifier);
258 }
259 }
260 return markElement(element);
261 }
262 };
263 public static final PositioningStrategy<JetReferenceExpression> FOR_UNRESOLVED_REFERENCE =
264 new PositioningStrategy<JetReferenceExpression>() {
265 @NotNull
266 @Override
267 public List<TextRange> mark(@NotNull JetReferenceExpression element) {
268 if (element instanceof JetArrayAccessExpression) {
269 List<TextRange> ranges = ((JetArrayAccessExpression) element).getBracketRanges();
270 if (!ranges.isEmpty()) {
271 return ranges;
272 }
273 }
274 return Collections.singletonList(element.getTextRange());
275 }
276 };
277
278 public static PositioningStrategy<JetModifierListOwner> modifierSetPosition(final JetKeywordToken... tokens) {
279 return new PositioningStrategy<JetModifierListOwner>() {
280 @NotNull
281 @Override
282 public List<TextRange> mark(@NotNull JetModifierListOwner modifierListOwner) {
283 JetModifierList modifierList = modifierListOwner.getModifierList();
284 assert modifierList != null : "No modifier list, but modifier has been found by the analyzer";
285
286 for (JetKeywordToken token : tokens) {
287 ASTNode node = modifierList.getModifierNode(token);
288 if (node != null) {
289 return markNode(node);
290 }
291 }
292 throw new IllegalStateException("None of the modifiers is found: " + Arrays.asList(tokens));
293 }
294 };
295 }
296
297 public static final PositioningStrategy<JetArrayAccessExpression> ARRAY_ACCESS = new PositioningStrategy<JetArrayAccessExpression>() {
298 @NotNull
299 @Override
300 public List<TextRange> mark(@NotNull JetArrayAccessExpression element) {
301 return markElement(element.getIndicesNode());
302 }
303 };
304
305 public static final PositioningStrategy<JetModifierListOwner> VISIBILITY_MODIFIER = new PositioningStrategy<JetModifierListOwner>() {
306 @NotNull
307 @Override
308 public List<TextRange> mark(@NotNull JetModifierListOwner element) {
309 List<JetModifierKeywordToken> visibilityTokens = Lists.newArrayList(
310 JetTokens.PRIVATE_KEYWORD, JetTokens.PROTECTED_KEYWORD, JetTokens.PUBLIC_KEYWORD, JetTokens.INTERNAL_KEYWORD);
311 List<TextRange> result = Lists.newArrayList();
312 for (JetModifierKeywordToken token : visibilityTokens) {
313 if (element.hasModifier(token)) {
314 //noinspection ConstantConditions
315 result.add(element.getModifierList().getModifierNode(token).getTextRange());
316 }
317 }
318
319 if (!result.isEmpty()) return result;
320
321 // Try to resolve situation when there's no visibility modifiers written before element
322
323 if (element instanceof PsiNameIdentifierOwner) {
324 PsiElement nameIdentifier = ((PsiNameIdentifierOwner) element).getNameIdentifier();
325 if (nameIdentifier != null) {
326 return ImmutableList.of(nameIdentifier.getTextRange());
327 }
328 }
329
330 if (element instanceof JetObjectDeclaration) {
331 return ImmutableList.of(((JetObjectDeclaration) element).getObjectKeyword().getTextRange());
332 }
333
334 if (element instanceof JetPropertyAccessor) {
335 return ImmutableList.of(((JetPropertyAccessor) element).getNamePlaceholder().getTextRange());
336 }
337
338 if (element instanceof JetClassInitializer) {
339 return ImmutableList.of(element.getTextRange());
340 }
341
342 if (element instanceof JetClassObject) {
343 JetObjectDeclaration objectDeclaration = ((JetClassObject) element).getObjectDeclaration();
344 return ImmutableList.of(objectDeclaration.getObjectKeyword().getTextRange());
345 }
346
347 throw new IllegalArgumentException(
348 String.format("Can't find text range for element '%s' with the text '%s'",
349 element.getClass().getCanonicalName(), element.getText()));
350 }
351 };
352
353 public static final PositioningStrategy<JetTypeProjection> VARIANCE_IN_PROJECTION = new PositioningStrategy<JetTypeProjection>() {
354 @NotNull
355 @Override
356 public List<TextRange> mark(@NotNull JetTypeProjection element) {
357 return markNode(element.getProjectionNode());
358 }
359 };
360
361 public static final PositioningStrategy<JetParameter> PARAMETER_DEFAULT_VALUE = new PositioningStrategy<JetParameter>() {
362 @NotNull
363 @Override
364 public List<TextRange> mark(@NotNull JetParameter element) {
365 return markNode(element.getDefaultValue().getNode());
366 }
367 };
368
369 public static final PositioningStrategy<PsiElement> CALL_ELEMENT = new PositioningStrategy<PsiElement>() {
370 @NotNull
371 @Override
372 public List<TextRange> mark(@NotNull PsiElement callElement) {
373 if (callElement instanceof JetCallElement) {
374 JetExpression calleeExpression = ((JetCallElement) callElement).getCalleeExpression();
375 if (calleeExpression != null) {
376 return markElement(calleeExpression);
377 }
378 }
379 return markElement(callElement);
380 }
381 };
382
383 public static final PositioningStrategy<JetDeclarationWithBody> DECLARATION_WITH_BODY = new PositioningStrategy<JetDeclarationWithBody>() {
384 @NotNull
385 @Override
386 public List<TextRange> mark(@NotNull JetDeclarationWithBody element) {
387 JetExpression bodyExpression = element.getBodyExpression();
388 if ((bodyExpression instanceof JetBlockExpression)) {
389 TextRange lastBracketRange = ((JetBlockExpression) bodyExpression).getLastBracketRange();
390 if (lastBracketRange != null) {
391 return markRange(lastBracketRange);
392 }
393 }
394 return markElement(element);
395 }
396
397 @Override
398 public boolean isValid(@NotNull JetDeclarationWithBody element) {
399 if (!super.isValid(element)) return false;
400
401 JetExpression bodyExpression = element.getBodyExpression();
402 if (!(bodyExpression instanceof JetBlockExpression)) return false;
403 if (((JetBlockExpression) bodyExpression).getLastBracketRange() == null) return false;
404 return true;
405 }
406 };
407
408 public static final PositioningStrategy<JetProperty> VAL_OR_VAR_NODE = new PositioningStrategy<JetProperty>() {
409 @NotNull
410 @Override
411 public List<TextRange> mark(@NotNull JetProperty property) {
412 return markNode(property.getValOrVarNode());
413 }
414 };
415
416 public static final PositioningStrategy<JetWhenEntry> ELSE_ENTRY = new PositioningStrategy<JetWhenEntry>() {
417 @NotNull
418 @Override
419 public List<TextRange> mark(@NotNull JetWhenEntry entry) {
420 PsiElement elseKeywordElement = entry.getElseKeywordElement();
421 assert elseKeywordElement != null;
422 return markElement(elseKeywordElement);
423 }
424 };
425
426 public static final PositioningStrategy<JetWhenExpression> WHEN_EXPRESSION = new PositioningStrategy<JetWhenExpression>() {
427 @NotNull
428 @Override
429 public List<TextRange> mark(@NotNull JetWhenExpression element) {
430 return markElement(element.getWhenKeywordElement());
431 }
432 };
433
434 public static final PositioningStrategy<JetWhenConditionInRange> WHEN_CONDITION_IN_RANGE =
435 new PositioningStrategy<JetWhenConditionInRange>() {
436 @NotNull
437 @Override
438 public List<TextRange> mark(@NotNull JetWhenConditionInRange condition) {
439 return markElement(condition.getOperationReference());
440 }
441 };
442
443 public static final PositioningStrategy<JetNullableType> NULLABLE_TYPE = new PositioningStrategy<JetNullableType>() {
444 @NotNull
445 @Override
446 public List<TextRange> mark(@NotNull JetNullableType element) {
447 return markNode(element.getQuestionMarkNode());
448 }
449 };
450
451 public static final PositioningStrategy<PsiElement> CALL_EXPRESSION = new PositioningStrategy<PsiElement>() {
452 @NotNull
453 @Override
454 public List<TextRange> mark(@NotNull PsiElement element) {
455 if (element instanceof JetCallExpression) {
456 JetCallExpression callExpression = (JetCallExpression) element;
457 PsiElement endElement;
458 JetTypeArgumentList typeArgumentList = callExpression.getTypeArgumentList();
459 JetExpression calleeExpression = callExpression.getCalleeExpression();
460 if (typeArgumentList != null) {
461 endElement = typeArgumentList;
462 }
463 else if (calleeExpression != null) {
464 endElement = calleeExpression;
465 }
466 else {
467 endElement = element;
468 }
469 return markRange(new TextRange(element.getTextRange().getStartOffset(), endElement.getTextRange().getEndOffset()));
470 }
471 return super.mark(element);
472 }
473 };
474
475 public static final PositioningStrategy<JetElement> VALUE_ARGUMENTS = new PositioningStrategy<JetElement>() {
476 @NotNull
477 @Override
478 public List<TextRange> mark(@NotNull JetElement element) {
479 if (element instanceof JetValueArgumentList) {
480 PsiElement rightParenthesis = ((JetValueArgumentList) element).getRightParenthesis();
481 if (rightParenthesis != null) {
482 return markElement(rightParenthesis);
483 }
484
485 }
486 return super.mark(element);
487 }
488 };
489
490 public static final PositioningStrategy<JetFunctionLiteral> FUNCTION_LITERAL_PARAMETERS = new PositioningStrategy<JetFunctionLiteral>() {
491 @NotNull
492 @Override
493 public List<TextRange> mark(@NotNull JetFunctionLiteral functionLiteral) {
494 JetParameterList valueParameterList = functionLiteral.getValueParameterList();
495 if (valueParameterList != null) {
496 return markElement(valueParameterList);
497 }
498 return markNode(functionLiteral.getOpenBraceNode());
499 }
500 };
501
502 public static final PositioningStrategy<JetElement> CUT_CHAR_QUOTES = new PositioningStrategy<JetElement>() {
503 @NotNull
504 @Override
505 public List<TextRange> mark(@NotNull JetElement element) {
506 if (element instanceof JetConstantExpression) {
507 if (element.getNode().getElementType() == JetNodeTypes.CHARACTER_CONSTANT) {
508 TextRange elementTextRange = element.getTextRange();
509 return Collections.singletonList(
510 TextRange.create(elementTextRange.getStartOffset() + 1, elementTextRange.getEndOffset() - 1));
511 }
512 }
513 return super.mark(element);
514 }
515 };
516
517 public static final PositioningStrategy<JetElement> LONG_LITERAL_SUFFIX = new PositioningStrategy<JetElement>() {
518 @NotNull
519 @Override
520 public List<TextRange> mark(@NotNull JetElement element) {
521 if (element instanceof JetConstantExpression) {
522 if (element.getNode().getElementType() == JetNodeTypes.INTEGER_CONSTANT) {
523 int endOffset = element.getTextRange().getEndOffset();
524 return Collections.singletonList(TextRange.create(endOffset - 1, endOffset));
525 }
526 }
527 return super.mark(element);
528 }
529 };
530
531 public static PositioningStrategy<PsiElement> markTextRangesFromDiagnostic(
532 @NotNull final Function1<Diagnostic, List<TextRange>> getTextRanges
533 ) {
534 return new PositioningStrategy<PsiElement>() {
535 @NotNull
536 @Override
537 public List<TextRange> markDiagnostic(@NotNull ParametrizedDiagnostic<? extends PsiElement> diagnostic) {
538 return getTextRanges.invoke(diagnostic);
539 }
540 };
541 }
542
543 private PositioningStrategies() {
544 }
545 }