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