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