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