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.cfg;
018
019 import com.google.common.collect.Lists;
020 import com.google.common.collect.Maps;
021 import com.google.common.collect.Sets;
022 import com.intellij.psi.PsiElement;
023 import com.intellij.psi.tree.IElementType;
024 import com.intellij.psi.util.PsiTreeUtil;
025 import org.jetbrains.annotations.NotNull;
026 import org.jetbrains.annotations.Nullable;
027 import org.jetbrains.jet.lang.cfg.pseudocode.*;
028 import org.jetbrains.jet.lang.cfg.PseudocodeTraverser.*;
029 import org.jetbrains.jet.lang.cfg.PseudocodeVariablesData.VariableInitState;
030 import org.jetbrains.jet.lang.cfg.PseudocodeVariablesData.VariableUseState;
031 import org.jetbrains.jet.lang.descriptors.*;
032 import org.jetbrains.jet.lang.diagnostics.AbstractDiagnosticFactory;
033 import org.jetbrains.jet.lang.diagnostics.Diagnostic;
034 import org.jetbrains.jet.lang.diagnostics.Errors;
035 import org.jetbrains.jet.lang.psi.*;
036 import org.jetbrains.jet.lang.resolve.BindingContext;
037 import org.jetbrains.jet.lang.resolve.BindingContextUtils;
038 import org.jetbrains.jet.lang.resolve.BindingTrace;
039 import org.jetbrains.jet.lang.resolve.DescriptorUtils;
040 import org.jetbrains.jet.lang.types.JetType;
041 import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
042 import org.jetbrains.jet.lexer.JetTokens;
043 import org.jetbrains.jet.plugin.JetMainDetector;
044
045 import java.util.*;
046
047 import static org.jetbrains.jet.lang.cfg.PseudocodeTraverser.TraversalOrder.BACKWARD;
048 import static org.jetbrains.jet.lang.cfg.PseudocodeTraverser.TraversalOrder.FORWARD;
049 import static org.jetbrains.jet.lang.cfg.PseudocodeVariablesData.VariableUseState.*;
050 import static org.jetbrains.jet.lang.diagnostics.Errors.*;
051 import static org.jetbrains.jet.lang.resolve.BindingContext.CAPTURED_IN_CLOSURE;
052 import static org.jetbrains.jet.lang.types.TypeUtils.NO_EXPECTED_TYPE;
053
054 public class JetFlowInformationProvider {
055
056 private final JetElement subroutine;
057 private final Pseudocode pseudocode;
058 private final PseudocodeVariablesData pseudocodeVariablesData;
059 private BindingTrace trace;
060
061 public JetFlowInformationProvider(
062 @NotNull JetElement declaration,
063 @NotNull BindingTrace trace) {
064
065 subroutine = declaration;
066 this.trace = trace;
067 pseudocode = new JetControlFlowProcessor(trace).generatePseudocode(declaration);
068 pseudocodeVariablesData = new PseudocodeVariablesData(pseudocode, trace.getBindingContext());
069 }
070
071 private void collectReturnExpressions(@NotNull final Collection<JetElement> returnedExpressions) {
072 final Set<Instruction> instructions = Sets.newHashSet(pseudocode.getInstructions());
073 SubroutineExitInstruction exitInstruction = pseudocode.getExitInstruction();
074 for (Instruction previousInstruction : exitInstruction.getPreviousInstructions()) {
075 previousInstruction.accept(new InstructionVisitor() {
076 @Override
077 public void visitReturnValue(ReturnValueInstruction instruction) {
078 if (instructions.contains(instruction)) { //exclude non-local return expressions
079 returnedExpressions.add(instruction.getElement());
080 }
081 }
082
083 @Override
084 public void visitReturnNoValue(ReturnNoValueInstruction instruction) {
085 if (instructions.contains(instruction)) {
086 returnedExpressions.add(instruction.getElement());
087 }
088 }
089
090
091 @Override
092 public void visitJump(AbstractJumpInstruction instruction) {
093 // Nothing
094 }
095
096 @Override
097 public void visitUnconditionalJump(UnconditionalJumpInstruction instruction) {
098 redirectToPrevInstructions(instruction);
099 }
100
101 private void redirectToPrevInstructions(Instruction instruction) {
102 for (Instruction previousInstruction : instruction.getPreviousInstructions()) {
103 previousInstruction.accept(this);
104 }
105 }
106
107 @Override
108 public void visitNondeterministicJump(NondeterministicJumpInstruction instruction) {
109 redirectToPrevInstructions(instruction);
110 }
111
112 @Override
113 public void visitInstruction(Instruction instruction) {
114 if (instruction instanceof JetElementInstruction) {
115 JetElementInstruction elementInstruction = (JetElementInstruction) instruction;
116 returnedExpressions.add(elementInstruction.getElement());
117 }
118 else {
119 throw new IllegalStateException(instruction + " precedes the exit point");
120 }
121 }
122 });
123 }
124 }
125
126 public void checkDefiniteReturn(final @NotNull JetType expectedReturnType) {
127 assert subroutine instanceof JetDeclarationWithBody;
128 JetDeclarationWithBody function = (JetDeclarationWithBody) subroutine;
129
130 JetExpression bodyExpression = function.getBodyExpression();
131 if (bodyExpression == null) return;
132
133 List<JetElement> returnedExpressions = Lists.newArrayList();
134 collectReturnExpressions(returnedExpressions);
135
136 final boolean blockBody = function.hasBlockBody();
137
138 final Set<JetElement> rootUnreachableElements = collectUnreachableCode();
139 for (JetElement element : rootUnreachableElements) {
140 trace.report(UNREACHABLE_CODE.on(element));
141 }
142
143 final boolean[] noReturnError = new boolean[] { false };
144 for (JetElement returnedExpression : returnedExpressions) {
145 returnedExpression.accept(new JetVisitorVoid() {
146 @Override
147 public void visitReturnExpression(JetReturnExpression expression) {
148 if (!blockBody) {
149 trace.report(RETURN_IN_FUNCTION_WITH_EXPRESSION_BODY.on(expression));
150 }
151 }
152
153 @Override
154 public void visitExpression(JetExpression expression) {
155 if (blockBody && expectedReturnType != NO_EXPECTED_TYPE && !KotlinBuiltIns.getInstance().isUnit(expectedReturnType) && !rootUnreachableElements.contains(expression)) {
156 noReturnError[0] = true;
157 }
158 }
159 });
160 }
161 if (noReturnError[0]) {
162 trace.report(NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY.on(function));
163 }
164 }
165
166 private Set<JetElement> collectUnreachableCode() {
167 Collection<JetElement> unreachableElements = Lists.newArrayList();
168 for (Instruction deadInstruction : pseudocode.getDeadInstructions()) {
169 if (deadInstruction instanceof JetElementInstruction &&
170 !(deadInstruction instanceof ReadUnitValueInstruction)) {
171 unreachableElements.add(((JetElementInstruction) deadInstruction).getElement());
172 }
173 }
174 // This is needed in order to highlight only '1 < 2' and not '1', '<' and '2' as well
175 return JetPsiUtil.findRootExpressions(unreachableElements);
176 }
177
178 ////////////////////////////////////////////////////////////////////////////////
179 // Uninitialized variables analysis
180
181 public void markUninitializedVariables() {
182 final Collection<VariableDescriptor> varWithUninitializedErrorGenerated = Sets.newHashSet();
183 final Collection<VariableDescriptor> varWithValReassignErrorGenerated = Sets.newHashSet();
184 final boolean processClassOrObject = subroutine instanceof JetClassOrObject;
185
186 Map<Instruction, Edges<Map<VariableDescriptor,VariableInitState>>> initializers = pseudocodeVariablesData.getVariableInitializers();
187 final Set<VariableDescriptor> declaredVariables = pseudocodeVariablesData.getDeclaredVariables(pseudocode, true);
188
189 final Map<Instruction, AbstractDiagnosticFactory> reportedDiagnosticMap = Maps.newHashMap();
190
191 PseudocodeTraverser.traverse(pseudocode, FORWARD, initializers, new InstructionDataAnalyzeStrategy<Map<VariableDescriptor, PseudocodeVariablesData.VariableInitState>>() {
192 @Override
193 public void execute(@NotNull Instruction instruction,
194 @Nullable Map<VariableDescriptor, VariableInitState> in,
195 @Nullable Map<VariableDescriptor, VariableInitState> out) {
196 assert in != null && out != null;
197 VariableInitContext ctxt = new VariableInitContext(instruction, reportedDiagnosticMap, in, out);
198 if (ctxt.variableDescriptor == null) return;
199 if (instruction instanceof ReadValueInstruction) {
200 JetElement element = ((ReadValueInstruction) instruction).getElement();
201 boolean error = checkBackingField(ctxt, element);
202 if (!error && declaredVariables.contains(ctxt.variableDescriptor)) {
203 checkIsInitialized(ctxt, element, varWithUninitializedErrorGenerated);
204 }
205 return;
206 }
207 if (!(instruction instanceof WriteValueInstruction)) return;
208 JetElement element = ((WriteValueInstruction) instruction).getlValue();
209 boolean error = checkBackingField(ctxt, element);
210 if (!(element instanceof JetExpression)) return;
211 if (!error) {
212 error = checkValReassignment(ctxt, (JetExpression) element, varWithValReassignErrorGenerated);
213 }
214 if (!error && processClassOrObject) {
215 error = checkAssignmentBeforeDeclaration(ctxt, (JetExpression) element);
216 }
217 if (!error && processClassOrObject) {
218 checkInitializationUsingBackingField(ctxt, (JetExpression) element);
219 }
220 }
221 });
222 }
223
224 public void recordInitializedVariables() {
225 Pseudocode pseudocode = pseudocodeVariablesData.getPseudocode();
226 Map<Instruction, Edges<Map<VariableDescriptor,VariableInitState>>> initializers = pseudocodeVariablesData.getVariableInitializers();
227 recordInitializedVariables(pseudocode, initializers);
228 for (LocalDeclarationInstruction instruction : pseudocode.getLocalDeclarations()) {
229 recordInitializedVariables(instruction.getBody(), initializers);
230 }
231 }
232
233 private void checkIsInitialized(
234 @NotNull VariableInitContext ctxt,
235 @NotNull JetElement element,
236 @NotNull Collection<VariableDescriptor> varWithUninitializedErrorGenerated
237 ) {
238 if (!(element instanceof JetSimpleNameExpression)) return;
239
240 boolean isInitialized = ctxt.exitInitState.isInitialized;
241 VariableDescriptor variableDescriptor = ctxt.variableDescriptor;
242 if (variableDescriptor instanceof PropertyDescriptor) {
243 if (!trace.get(BindingContext.BACKING_FIELD_REQUIRED, (PropertyDescriptor) variableDescriptor)) {
244 isInitialized = true;
245 }
246 }
247 if (!isInitialized && !varWithUninitializedErrorGenerated.contains(variableDescriptor)) {
248 if (!(variableDescriptor instanceof PropertyDescriptor)) {
249 varWithUninitializedErrorGenerated.add(variableDescriptor);
250 }
251 if (variableDescriptor instanceof ValueParameterDescriptor) {
252 report(Errors.UNINITIALIZED_PARAMETER.on((JetSimpleNameExpression) element,
253 (ValueParameterDescriptor) variableDescriptor), ctxt);
254 }
255 else {
256 report(Errors.UNINITIALIZED_VARIABLE.on((JetSimpleNameExpression) element, variableDescriptor), ctxt);
257 }
258 }
259 }
260
261 private boolean checkValReassignment(
262 @NotNull VariableInitContext ctxt,
263 @NotNull JetExpression expression,
264 @NotNull Collection<VariableDescriptor> varWithValReassignErrorGenerated
265 ) {
266 VariableDescriptor variableDescriptor = ctxt.variableDescriptor;
267 if (JetPsiUtil.isBackingFieldReference(expression) && variableDescriptor instanceof PropertyDescriptor) {
268 PropertyDescriptor propertyDescriptor = (PropertyDescriptor) variableDescriptor;
269 JetPropertyAccessor accessor = PsiTreeUtil.getParentOfType(expression, JetPropertyAccessor.class);
270 if (accessor != null) {
271 DeclarationDescriptor accessorDescriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, accessor);
272 if (propertyDescriptor.getGetter() == accessorDescriptor) {
273 //val can be reassigned through backing field inside its own getter
274 return false;
275 }
276 }
277 }
278
279 boolean isInitializedNotHere = ctxt.enterInitState.isInitialized;
280 boolean hasBackingField = true;
281 if (variableDescriptor instanceof PropertyDescriptor) {
282 hasBackingField = trace.get(BindingContext.BACKING_FIELD_REQUIRED, (PropertyDescriptor) variableDescriptor);
283 }
284 if (variableDescriptor.isVar() && variableDescriptor instanceof PropertyDescriptor) {
285 DeclarationDescriptor descriptor = BindingContextUtils.getEnclosingDescriptor(trace.getBindingContext(), expression);
286 PropertySetterDescriptor setterDescriptor = ((PropertyDescriptor) variableDescriptor).getSetter();
287 if (Visibilities.isVisible(variableDescriptor, descriptor) && !Visibilities.isVisible(setterDescriptor, descriptor) && setterDescriptor != null) {
288 report(Errors.INVISIBLE_SETTER.on(expression, variableDescriptor, setterDescriptor.getVisibility(),
289 variableDescriptor.getContainingDeclaration()), ctxt);
290 return true;
291 }
292 }
293 if ((isInitializedNotHere || !hasBackingField) && !variableDescriptor.isVar() && !varWithValReassignErrorGenerated.contains(variableDescriptor)) {
294 boolean hasReassignMethodReturningUnit = false;
295 JetSimpleNameExpression operationReference = null;
296 PsiElement parent = expression.getParent();
297 if (parent instanceof JetBinaryExpression) {
298 operationReference = ((JetBinaryExpression) parent).getOperationReference();
299 }
300 else if (parent instanceof JetUnaryExpression) {
301 operationReference = ((JetUnaryExpression) parent).getOperationReference();
302 }
303 if (operationReference != null) {
304 DeclarationDescriptor descriptor = trace.get(BindingContext.REFERENCE_TARGET, operationReference);
305 if (descriptor instanceof FunctionDescriptor) {
306 if (KotlinBuiltIns.getInstance().isUnit(((FunctionDescriptor) descriptor).getReturnType())) {
307 hasReassignMethodReturningUnit = true;
308 }
309 }
310 if (descriptor == null) {
311 Collection<? extends DeclarationDescriptor> descriptors = trace.get(BindingContext.AMBIGUOUS_REFERENCE_TARGET, operationReference);
312 if (descriptors != null) {
313 for (DeclarationDescriptor referenceDescriptor : descriptors) {
314 if (KotlinBuiltIns.getInstance().isUnit(((FunctionDescriptor) referenceDescriptor).getReturnType())) {
315 hasReassignMethodReturningUnit = true;
316 }
317 }
318 }
319 }
320 }
321 if (!hasReassignMethodReturningUnit) {
322 varWithValReassignErrorGenerated.add(variableDescriptor);
323 report(Errors.VAL_REASSIGNMENT.on(expression, variableDescriptor), ctxt);
324 return true;
325 }
326 }
327 return false;
328 }
329
330 private boolean checkAssignmentBeforeDeclaration(@NotNull VariableInitContext ctxt, @NotNull JetExpression expression) {
331 if (!ctxt.enterInitState.isDeclared && !ctxt.exitInitState.isDeclared && !ctxt.enterInitState.isInitialized && ctxt.exitInitState.isInitialized) {
332 report(Errors.INITIALIZATION_BEFORE_DECLARATION.on(expression, ctxt.variableDescriptor), ctxt);
333 return true;
334 }
335 return false;
336 }
337
338 private boolean checkInitializationUsingBackingField(@NotNull VariableInitContext ctxt, @NotNull JetExpression expression) {
339 VariableDescriptor variableDescriptor = ctxt.variableDescriptor;
340 if (variableDescriptor instanceof PropertyDescriptor && !ctxt.enterInitState.isInitialized && ctxt.exitInitState.isInitialized) {
341 if (!variableDescriptor.isVar()) return false;
342 if (!trace.get(BindingContext.BACKING_FIELD_REQUIRED, (PropertyDescriptor) variableDescriptor)) return false;
343 PsiElement property = BindingContextUtils.descriptorToDeclaration(trace.getBindingContext(), variableDescriptor);
344 assert property instanceof JetProperty;
345 if (((PropertyDescriptor) variableDescriptor).getModality() == Modality.FINAL && ((JetProperty) property).getSetter() == null) return false;
346 JetExpression variable = expression;
347 if (expression instanceof JetDotQualifiedExpression) {
348 if (((JetDotQualifiedExpression) expression).getReceiverExpression() instanceof JetThisExpression) {
349 variable = ((JetDotQualifiedExpression) expression).getSelectorExpression();
350 }
351 }
352 if (variable instanceof JetSimpleNameExpression) {
353 JetSimpleNameExpression simpleNameExpression = (JetSimpleNameExpression) variable;
354 if (simpleNameExpression.getReferencedNameElementType() != JetTokens.FIELD_IDENTIFIER) {
355 if (((PropertyDescriptor) variableDescriptor).getModality() != Modality.FINAL) {
356 report(Errors.INITIALIZATION_USING_BACKING_FIELD_OPEN_SETTER.on(expression, variableDescriptor), ctxt);
357 }
358 else {
359 report(Errors.INITIALIZATION_USING_BACKING_FIELD_CUSTOM_SETTER.on(expression, variableDescriptor), ctxt);
360 }
361 return true;
362 }
363 }
364 }
365 return false;
366 }
367
368 private boolean checkBackingField(@NotNull VariableContext cxtx, @NotNull JetElement element) {
369 VariableDescriptor variableDescriptor = cxtx.variableDescriptor;
370 boolean[] error = new boolean[1];
371 if (isCorrectBackingFieldReference((JetElement) element.getParent(), cxtx, error, false)) return false; // this expression has been already checked
372 if (!isCorrectBackingFieldReference(element, cxtx, error, true)) return false;
373 if (error[0]) return true;
374 if (!(variableDescriptor instanceof PropertyDescriptor)) {
375 report(Errors.NOT_PROPERTY_BACKING_FIELD.on(element), cxtx);
376 return true;
377 }
378 PsiElement property = BindingContextUtils.descriptorToDeclaration(trace.getBindingContext(), variableDescriptor);
379 boolean insideSelfAccessors = PsiTreeUtil.isAncestor(property, element, false);
380 if (!trace.get(BindingContext.BACKING_FIELD_REQUIRED, (PropertyDescriptor) variableDescriptor) &&
381 !insideSelfAccessors) { // not to generate error in accessors of abstract properties, there is one: declared accessor of abstract property
382
383 if (((PropertyDescriptor) variableDescriptor).getModality() == Modality.ABSTRACT) {
384 report(NO_BACKING_FIELD_ABSTRACT_PROPERTY.on(element), cxtx);
385 }
386 else {
387 report(NO_BACKING_FIELD_CUSTOM_ACCESSORS.on(element), cxtx);
388 }
389 return true;
390 }
391 if (insideSelfAccessors) return false;
392
393 DeclarationDescriptor declarationDescriptor = BindingContextUtils.getEnclosingDescriptor(trace.getBindingContext(), element);
394
395 DeclarationDescriptor containingDeclaration = variableDescriptor.getContainingDeclaration();
396 if ((containingDeclaration instanceof ClassDescriptor) && DescriptorUtils.isAncestor(containingDeclaration, declarationDescriptor, false)) {
397 return false;
398 }
399 report(Errors.INACCESSIBLE_BACKING_FIELD.on(element), cxtx);
400 return true;
401 }
402
403 private boolean isCorrectBackingFieldReference(@Nullable JetElement element, VariableContext ctxt, boolean[] error, boolean reportError) {
404 error[0] = false;
405 if (JetPsiUtil.isBackingFieldReference(element)) {
406 return true;
407 }
408 if (element instanceof JetDotQualifiedExpression && isCorrectBackingFieldReference(
409 ((JetDotQualifiedExpression) element).getSelectorExpression(), ctxt, error, false)) {
410 if (((JetDotQualifiedExpression) element).getReceiverExpression() instanceof JetThisExpression) {
411 return true;
412 }
413 error[0] = true;
414 if (reportError) {
415 report(Errors.INACCESSIBLE_BACKING_FIELD.on(element), ctxt);
416 }
417 }
418 return false;
419 }
420
421 private void recordInitializedVariables(@NotNull Pseudocode pseudocode, @NotNull Map<Instruction, Edges<Map<VariableDescriptor,PseudocodeVariablesData.VariableInitState>>> initializersMap) {
422 Edges<Map<VariableDescriptor, VariableInitState>> initializers = initializersMap.get(pseudocode.getExitInstruction());
423 Set<VariableDescriptor> declaredVariables = pseudocodeVariablesData.getDeclaredVariables(pseudocode, false);
424 for (VariableDescriptor variable : declaredVariables) {
425 if (variable instanceof PropertyDescriptor) {
426 PseudocodeVariablesData.VariableInitState variableInitState = initializers.in.get(variable);
427 if (variableInitState == null) return;
428 trace.record(BindingContext.IS_INITIALIZED, (PropertyDescriptor) variable, variableInitState.isInitialized);
429 }
430 }
431 }
432
433 ////////////////////////////////////////////////////////////////////////////////
434 // "Unused variable" & "unused value" analyses
435
436 public void markUnusedVariables() {
437 Map<Instruction, Edges<Map<VariableDescriptor, VariableUseState>>> variableStatusData = pseudocodeVariablesData.getVariableUseStatusData();
438 final Map<Instruction, AbstractDiagnosticFactory> reportedDiagnosticMap = Maps.newHashMap();
439 InstructionDataAnalyzeStrategy<Map<VariableDescriptor, VariableUseState>> variableStatusAnalyzeStrategy =
440 new InstructionDataAnalyzeStrategy<Map<VariableDescriptor, PseudocodeVariablesData.VariableUseState>>() {
441 @Override
442 public void execute(@NotNull Instruction instruction,
443 @Nullable Map<VariableDescriptor, VariableUseState> in,
444 @Nullable Map<VariableDescriptor, VariableUseState> out) {
445
446 assert in != null && out != null;
447 VariableContext ctxt = new VariableUseContext(instruction, reportedDiagnosticMap, in, out);
448 Set<VariableDescriptor> declaredVariables = pseudocodeVariablesData.getDeclaredVariables(instruction.getOwner(), false);
449 VariableDescriptor variableDescriptor = PseudocodeUtil.extractVariableDescriptorIfAny(instruction, false,
450 trace.getBindingContext());
451 if (variableDescriptor == null || !declaredVariables.contains(variableDescriptor) ||
452 !DescriptorUtils.isLocal(variableDescriptor.getContainingDeclaration(), variableDescriptor)) return;
453 PseudocodeVariablesData.VariableUseState variableUseState = in.get(variableDescriptor);
454 if (instruction instanceof WriteValueInstruction) {
455 if (trace.get(CAPTURED_IN_CLOSURE, variableDescriptor) != null) return;
456 JetElement element = ((WriteValueInstruction) instruction).getElement();
457 if (variableUseState != LAST_READ) {
458 if (element instanceof JetBinaryExpression &&
459 ((JetBinaryExpression) element).getOperationToken() == JetTokens.EQ) {
460 JetExpression right = ((JetBinaryExpression) element).getRight();
461 if (right != null) {
462 report(Errors.UNUSED_VALUE.on(right, right, variableDescriptor), ctxt);
463 }
464 }
465 else if (element instanceof JetPostfixExpression) {
466 IElementType operationToken = ((JetPostfixExpression) element).getOperationReference().getReferencedNameElementType();
467 if (operationToken == JetTokens.PLUSPLUS || operationToken == JetTokens.MINUSMINUS) {
468 report(Errors.UNUSED_CHANGED_VALUE.on(element, element), ctxt);
469 }
470 }
471 }
472 }
473 else if (instruction instanceof VariableDeclarationInstruction) {
474 JetDeclaration element = ((VariableDeclarationInstruction) instruction).getVariableDeclarationElement();
475 if (!(element instanceof JetNamedDeclaration)) return;
476 PsiElement nameIdentifier = ((JetNamedDeclaration) element).getNameIdentifier();
477 if (nameIdentifier == null) return;
478 if (!VariableUseState.isUsed(variableUseState)) {
479 if (JetPsiUtil.isVariableNotParameterDeclaration(element)) {
480 report(Errors.UNUSED_VARIABLE.on((JetNamedDeclaration) element, variableDescriptor), ctxt);
481 }
482 else if (element instanceof JetParameter) {
483 PsiElement psiElement = element.getParent().getParent();
484 if (psiElement instanceof JetFunction) {
485 boolean isMain = (psiElement instanceof JetNamedFunction) && JetMainDetector.isMain((JetNamedFunction) psiElement);
486 if (psiElement instanceof JetFunctionLiteral) return;
487 DeclarationDescriptor descriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, psiElement);
488 assert descriptor instanceof FunctionDescriptor : psiElement.getText();
489 FunctionDescriptor functionDescriptor = (FunctionDescriptor) descriptor;
490 if (!isMain && !functionDescriptor.getModality().isOverridable() && functionDescriptor.getOverriddenDescriptors().isEmpty()) {
491 report(Errors.UNUSED_PARAMETER.on((JetParameter) element, variableDescriptor), ctxt);
492 }
493 }
494 }
495 }
496 else if (variableUseState == ONLY_WRITTEN_NEVER_READ && JetPsiUtil.isVariableNotParameterDeclaration(element)) {
497 report(Errors.ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE.on((JetNamedDeclaration) element, variableDescriptor), ctxt);
498 }
499 else if (variableUseState == LAST_WRITTEN && element instanceof JetVariableDeclaration) {
500 if (element instanceof JetProperty) {
501 JetExpression initializer = ((JetProperty) element).getInitializer();
502 if (initializer != null) {
503 report(Errors.VARIABLE_WITH_REDUNDANT_INITIALIZER.on(initializer, variableDescriptor), ctxt);
504 }
505 }
506 else if (element instanceof JetMultiDeclarationEntry) {
507 report(VARIABLE_WITH_REDUNDANT_INITIALIZER.on(element, variableDescriptor), ctxt);
508 }
509 }
510 }
511 }
512 };
513 PseudocodeTraverser.traverse(pseudocode, BACKWARD, variableStatusData, variableStatusAnalyzeStrategy);
514 }
515
516 ////////////////////////////////////////////////////////////////////////////////
517 // "Unused literals" in block
518
519 public void markUnusedLiteralsInBlock() {
520 assert pseudocode != null;
521 final Map<Instruction, AbstractDiagnosticFactory> reportedDiagnosticMap = Maps.newHashMap();
522 PseudocodeTraverser.traverse(
523 pseudocode, FORWARD, new InstructionAnalyzeStrategy() {
524 @Override
525 public void execute(@NotNull Instruction instruction) {
526 if (!(instruction instanceof ReadValueInstruction)) return;
527 VariableContext ctxt = new VariableContext(instruction, reportedDiagnosticMap);
528 JetElement element =
529 ((ReadValueInstruction) instruction).getElement();
530 if (!(element instanceof JetFunctionLiteralExpression
531 || element instanceof JetConstantExpression
532 || element instanceof JetStringTemplateExpression
533 || element instanceof JetSimpleNameExpression)) {
534 return;
535 }
536 PsiElement parent = element.getParent();
537 if (parent instanceof JetBlockExpression) {
538 if (!JetPsiUtil.isImplicitlyUsed(element)) {
539 if (element instanceof JetFunctionLiteralExpression) {
540 report(Errors.UNUSED_FUNCTION_LITERAL.on((JetFunctionLiteralExpression) element), ctxt);
541 }
542 else {
543 report(Errors.UNUSED_EXPRESSION.on(element), ctxt);
544 }
545 }
546 }
547 }
548 });
549 }
550
551 ////////////////////////////////////////////////////////////////////////////////
552 // Utility classes and methods
553
554 /**
555 * The method provides reporting of the same diagnostic only once for copied instructions
556 * (depends on whether it should be reported for all or only for one of the copies)
557 */
558 private void report(
559 @NotNull Diagnostic diagnostic,
560 @NotNull VariableContext ctxt
561 ) {
562 Instruction instruction = ctxt.instruction;
563 if (instruction.getCopies().isEmpty()) {
564 trace.report(diagnostic);
565 return;
566 }
567 Map<Instruction, AbstractDiagnosticFactory> previouslyReported = ctxt.reportedDiagnosticMap;
568 previouslyReported.put(instruction, diagnostic.getFactory());
569
570 boolean alreadyReported = false;
571 boolean sameErrorForAllCopies = true;
572 for (Instruction copy : instruction.getCopies()) {
573 AbstractDiagnosticFactory previouslyReportedErrorFactory = previouslyReported.get(copy);
574 if (previouslyReportedErrorFactory != null) {
575 alreadyReported = true;
576 }
577
578 if (previouslyReportedErrorFactory != diagnostic.getFactory()) {
579 sameErrorForAllCopies = false;
580 }
581 }
582
583 if (mustBeReportedOnAllCopies(diagnostic.getFactory())) {
584 if (sameErrorForAllCopies) {
585 trace.report(diagnostic);
586 }
587 }
588 else {
589 //only one reporting required
590 if (!alreadyReported) {
591 trace.report(diagnostic);
592 }
593 }
594 }
595
596 private static boolean mustBeReportedOnAllCopies(@NotNull AbstractDiagnosticFactory diagnosticFactory) {
597 return diagnosticFactory == UNUSED_VARIABLE
598 || diagnosticFactory == UNUSED_PARAMETER
599 || diagnosticFactory == UNUSED_CHANGED_VALUE;
600 }
601
602
603
604 private class VariableContext {
605 final Map<Instruction, AbstractDiagnosticFactory> reportedDiagnosticMap;
606 final Instruction instruction;
607 final VariableDescriptor variableDescriptor;
608
609 private VariableContext(
610 @NotNull Instruction instruction,
611 @NotNull Map<Instruction, AbstractDiagnosticFactory> map
612 ) {
613 this.instruction = instruction;
614 reportedDiagnosticMap = map;
615 variableDescriptor = PseudocodeUtil.extractVariableDescriptorIfAny(instruction, true, trace.getBindingContext());
616 }
617 }
618
619 private class VariableInitContext extends VariableContext {
620 final VariableInitState enterInitState;
621 final VariableInitState exitInitState;
622
623 private VariableInitContext(
624 @NotNull Instruction instruction,
625 @NotNull Map<Instruction, AbstractDiagnosticFactory> map,
626 @NotNull Map<VariableDescriptor, VariableInitState> in,
627 @NotNull Map<VariableDescriptor, VariableInitState> out
628 ) {
629 super(instruction, map);
630 enterInitState = variableDescriptor != null ? in.get(variableDescriptor) : null;
631 exitInitState = variableDescriptor != null ? out.get(variableDescriptor) : null;
632 }
633 }
634
635 private class VariableUseContext extends VariableContext {
636 final VariableUseState enterUseState;
637 final VariableUseState exitUseState;
638
639
640 private VariableUseContext(
641 @NotNull Instruction instruction,
642 @NotNull Map<Instruction, AbstractDiagnosticFactory> map,
643 @NotNull Map<VariableDescriptor, VariableUseState> in,
644 @NotNull Map<VariableDescriptor, VariableUseState> out
645 ) {
646 super(instruction, map);
647 enterUseState = variableDescriptor != null ? in.get(variableDescriptor) : null;
648 exitUseState = variableDescriptor != null ? out.get(variableDescriptor) : null;
649 }
650 }
651 }