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