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