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 JetPropertyAccessor) {
289 return ImmutableList.of(((JetPropertyAccessor) element).getNamePlaceholder().getTextRange());
290 }
291
292 if (element instanceof JetClassInitializer) {
293 return ImmutableList.of(element.getTextRange());
294 }
295
296 if (element instanceof JetClassObject) {
297 JetObjectDeclaration objectDeclaration = ((JetClassObject) element).getObjectDeclaration();
298 return ImmutableList.of(objectDeclaration.getObjectKeyword().getTextRange());
299 }
300
301 throw new IllegalArgumentException(
302 String.format("Can't find text range for element '%s' with the text '%s'",
303 element.getClass().getCanonicalName(), element.getText()));
304 }
305 };
306
307 public static final PositioningStrategy<JetTypeProjection> VARIANCE_IN_PROJECTION = new PositioningStrategy<JetTypeProjection>() {
308 @NotNull
309 @Override
310 public List<TextRange> mark(@NotNull JetTypeProjection element) {
311 return markNode(element.getProjectionNode());
312 }
313 };
314
315 public static final PositioningStrategy<JetParameter> PARAMETER_DEFAULT_VALUE = new PositioningStrategy<JetParameter>() {
316 @NotNull
317 @Override
318 public List<TextRange> mark(@NotNull JetParameter element) {
319 return markNode(element.getDefaultValue().getNode());
320 }
321 };
322
323 public static final PositioningStrategy<PsiElement> CALL_ELEMENT = new PositioningStrategy<PsiElement>() {
324 @NotNull
325 @Override
326 public List<TextRange> mark(@NotNull PsiElement callElement) {
327 if (callElement instanceof JetCallElement) {
328 JetExpression calleeExpression = ((JetCallElement) callElement).getCalleeExpression();
329 if (calleeExpression != null) {
330 return markElement(calleeExpression);
331 }
332 }
333 return markElement(callElement);
334 }
335 };
336
337 public static final PositioningStrategy<JetDeclarationWithBody> DECLARATION_WITH_BODY = new PositioningStrategy<JetDeclarationWithBody>() {
338 @NotNull
339 @Override
340 public List<TextRange> mark(@NotNull JetDeclarationWithBody element) {
341 JetExpression bodyExpression = element.getBodyExpression();
342 if ((bodyExpression instanceof JetBlockExpression)) {
343 TextRange lastBracketRange = ((JetBlockExpression) bodyExpression).getLastBracketRange();
344 if (lastBracketRange != null) {
345 return markRange(lastBracketRange);
346 }
347 }
348 return markElement(element);
349 }
350
351 @Override
352 public boolean isValid(@NotNull JetDeclarationWithBody element) {
353 if (!super.isValid(element)) return false;
354
355 JetExpression bodyExpression = element.getBodyExpression();
356 if (!(bodyExpression instanceof JetBlockExpression)) return false;
357 if (((JetBlockExpression) bodyExpression).getLastBracketRange() == null) return false;
358 return true;
359 }
360 };
361
362 public static final PositioningStrategy<JetProperty> VAL_OR_VAR_NODE = new PositioningStrategy<JetProperty>() {
363 @NotNull
364 @Override
365 public List<TextRange> mark(@NotNull JetProperty property) {
366 return markNode(property.getValOrVarNode());
367 }
368 };
369
370 public static final PositioningStrategy<JetWhenEntry> ELSE_ENTRY = new PositioningStrategy<JetWhenEntry>() {
371 @NotNull
372 @Override
373 public List<TextRange> mark(@NotNull JetWhenEntry entry) {
374 PsiElement elseKeywordElement = entry.getElseKeywordElement();
375 assert elseKeywordElement != null;
376 return markElement(elseKeywordElement);
377 }
378 };
379
380 public static final PositioningStrategy<JetWhenExpression> WHEN_EXPRESSION = new PositioningStrategy<JetWhenExpression>() {
381 @NotNull
382 @Override
383 public List<TextRange> mark(@NotNull JetWhenExpression element) {
384 return markElement(element.getWhenKeywordElement());
385 }
386 };
387
388 public static final PositioningStrategy<JetWhenConditionInRange> WHEN_CONDITION_IN_RANGE =
389 new PositioningStrategy<JetWhenConditionInRange>() {
390 @NotNull
391 @Override
392 public List<TextRange> mark(@NotNull JetWhenConditionInRange condition) {
393 return markElement(condition.getOperationReference());
394 }
395 };
396
397 public static final PositioningStrategy<JetNullableType> NULLABLE_TYPE = new PositioningStrategy<JetNullableType>() {
398 @NotNull
399 @Override
400 public List<TextRange> mark(@NotNull JetNullableType element) {
401 return markNode(element.getQuestionMarkNode());
402 }
403 };
404
405 public static final PositioningStrategy<PsiElement> CALL_EXPRESSION = new PositioningStrategy<PsiElement>() {
406 @NotNull
407 @Override
408 public List<TextRange> mark(@NotNull PsiElement element) {
409 if (element instanceof JetCallExpression) {
410 JetCallExpression callExpression = (JetCallExpression) element;
411 PsiElement endElement;
412 JetTypeArgumentList typeArgumentList = callExpression.getTypeArgumentList();
413 JetExpression calleeExpression = callExpression.getCalleeExpression();
414 if (typeArgumentList != null) {
415 endElement = typeArgumentList;
416 }
417 else if (calleeExpression != null) {
418 endElement = calleeExpression;
419 }
420 else {
421 endElement = element;
422 }
423 return markRange(new TextRange(element.getTextRange().getStartOffset(), endElement.getTextRange().getEndOffset()));
424 }
425 return super.mark(element);
426 }
427 };
428
429 public static final PositioningStrategy<JetElement> VALUE_ARGUMENTS = new PositioningStrategy<JetElement>() {
430 @NotNull
431 @Override
432 public List<TextRange> mark(@NotNull JetElement element) {
433 if (element instanceof JetValueArgumentList) {
434 PsiElement rightParenthesis = ((JetValueArgumentList) element).getRightParenthesis();
435 if (rightParenthesis != null) {
436 return markElement(rightParenthesis);
437 }
438
439 }
440 return super.mark(element);
441 }
442 };
443
444 public static final PositioningStrategy<JetFunctionLiteral> FUNCTION_LITERAL_PARAMETERS = new PositioningStrategy<JetFunctionLiteral>() {
445 @NotNull
446 @Override
447 public List<TextRange> mark(@NotNull JetFunctionLiteral functionLiteral) {
448 JetParameterList valueParameterList = functionLiteral.getValueParameterList();
449 if (valueParameterList != null) {
450 return markElement(valueParameterList);
451 }
452 return markNode(functionLiteral.getOpenBraceNode());
453 }
454 };
455
456 public static final PositioningStrategy<JetElement> CUT_CHAR_QUOTES = new PositioningStrategy<JetElement>() {
457 @NotNull
458 @Override
459 public List<TextRange> mark(@NotNull JetElement element) {
460 if (element instanceof JetConstantExpression) {
461 if (element.getNode().getElementType() == JetNodeTypes.CHARACTER_CONSTANT) {
462 TextRange elementTextRange = element.getTextRange();
463 return Collections.singletonList(
464 TextRange.create(elementTextRange.getStartOffset() + 1, elementTextRange.getEndOffset() - 1));
465 }
466 }
467 return super.mark(element);
468 }
469 };
470
471 public static final PositioningStrategy<JetElement> LONG_LITERAL_SUFFIX = new PositioningStrategy<JetElement>() {
472 @NotNull
473 @Override
474 public List<TextRange> mark(@NotNull JetElement element) {
475 if (element instanceof JetConstantExpression) {
476 if (element.getNode().getElementType() == JetNodeTypes.INTEGER_CONSTANT) {
477 int endOffset = element.getTextRange().getEndOffset();
478 return Collections.singletonList(TextRange.create(endOffset - 1, endOffset));
479 }
480 }
481 return super.mark(element);
482 }
483 };
484
485 private PositioningStrategies() {
486 }
487 }