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