001 /*
002 * Copyright 2010-2015 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.kotlin.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 kotlin.Unit;
026 import kotlin.jvm.functions.Function1;
027 import kotlin.jvm.functions.Function3;
028 import org.jetbrains.annotations.NotNull;
029 import org.jetbrains.annotations.Nullable;
030 import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
031 import org.jetbrains.kotlin.cfg.PseudocodeVariablesData.VariableControlFlowState;
032 import org.jetbrains.kotlin.cfg.PseudocodeVariablesData.VariableUseState;
033 import org.jetbrains.kotlin.cfg.pseudocode.PseudoValue;
034 import org.jetbrains.kotlin.cfg.pseudocode.Pseudocode;
035 import org.jetbrains.kotlin.cfg.pseudocode.PseudocodeUtil;
036 import org.jetbrains.kotlin.cfg.pseudocode.PseudocodeUtilsKt;
037 import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction;
038 import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionVisitor;
039 import org.jetbrains.kotlin.cfg.pseudocode.instructions.KtElementInstruction;
040 import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.*;
041 import org.jetbrains.kotlin.cfg.pseudocode.instructions.jumps.*;
042 import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.LocalFunctionDeclarationInstruction;
043 import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.MarkInstruction;
044 import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.SubroutineExitInstruction;
045 import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.VariableDeclarationInstruction;
046 import org.jetbrains.kotlin.cfg.pseudocodeTraverser.Edges;
047 import org.jetbrains.kotlin.cfg.pseudocodeTraverser.PseudocodeTraverserKt;
048 import org.jetbrains.kotlin.cfg.pseudocodeTraverser.TraversalOrder;
049 import org.jetbrains.kotlin.descriptors.*;
050 import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptorKt;
051 import org.jetbrains.kotlin.diagnostics.Diagnostic;
052 import org.jetbrains.kotlin.diagnostics.DiagnosticFactory;
053 import org.jetbrains.kotlin.diagnostics.Errors;
054 import org.jetbrains.kotlin.idea.MainFunctionDetector;
055 import org.jetbrains.kotlin.lexer.KtTokens;
056 import org.jetbrains.kotlin.psi.*;
057 import org.jetbrains.kotlin.resolve.*;
058 import org.jetbrains.kotlin.resolve.bindingContextUtil.BindingContextUtilsKt;
059 import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt;
060 import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
061 import org.jetbrains.kotlin.resolve.calls.resolvedCallUtil.ResolvedCallUtilKt;
062 import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
063 import org.jetbrains.kotlin.types.KotlinType;
064 import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils;
065
066 import java.util.*;
067
068 import static org.jetbrains.kotlin.cfg.PseudocodeVariablesData.VariableUseState.*;
069 import static org.jetbrains.kotlin.cfg.TailRecursionKind.*;
070 import static org.jetbrains.kotlin.diagnostics.Errors.*;
071 import static org.jetbrains.kotlin.diagnostics.Errors.UNREACHABLE_CODE;
072 import static org.jetbrains.kotlin.resolve.BindingContext.*;
073 import static org.jetbrains.kotlin.types.TypeUtils.NO_EXPECTED_TYPE;
074 import static org.jetbrains.kotlin.types.TypeUtils.noExpectedType;
075
076 public class ControlFlowInformationProvider {
077
078 private final KtElement subroutine;
079 private final Pseudocode pseudocode;
080 private final BindingTrace trace;
081 private PseudocodeVariablesData pseudocodeVariablesData;
082
083 private ControlFlowInformationProvider(
084 @NotNull KtElement declaration,
085 @NotNull BindingTrace trace,
086 @NotNull Pseudocode pseudocode
087 ) {
088 this.subroutine = declaration;
089 this.trace = trace;
090 this.pseudocode = pseudocode;
091 }
092
093 public ControlFlowInformationProvider(
094 @NotNull KtElement declaration,
095 @NotNull BindingTrace trace
096 ) {
097 this(declaration, trace, new ControlFlowProcessor(trace).generatePseudocode(declaration));
098 }
099
100 public PseudocodeVariablesData getPseudocodeVariablesData() {
101 if (pseudocodeVariablesData == null) {
102 pseudocodeVariablesData = new PseudocodeVariablesData(pseudocode, trace.getBindingContext());
103 }
104 return pseudocodeVariablesData;
105 }
106
107 public void checkForLocalClassOrObjectMode() {
108 // Local classes and objects are analyzed twice: when TopDownAnalyzer processes it and as a part of its container.
109 // Almost all checks can be done when the container is analyzed
110 // except recording initialized variables (this information is needed for DeclarationChecker).
111 recordInitializedVariables();
112 }
113
114 public void checkDeclaration() {
115
116 recordInitializedVariables();
117
118 checkLocalFunctions();
119
120 markUninitializedVariables();
121
122 markUnusedVariables();
123
124 markStatements();
125
126 markUnusedExpressions();
127
128 markIfWithoutElse();
129
130 markWhenWithoutElse();
131 }
132
133 public void checkFunction(@Nullable KotlinType expectedReturnType) {
134 UnreachableCode unreachableCode = collectUnreachableCode();
135 reportUnreachableCode(unreachableCode);
136
137 if (subroutine instanceof KtFunctionLiteral) return;
138
139 checkDefiniteReturn(expectedReturnType != null ? expectedReturnType : NO_EXPECTED_TYPE, unreachableCode);
140
141 markTailCalls();
142 }
143
144 private void collectReturnExpressions(@NotNull final Collection<KtElement> returnedExpressions) {
145 final Set<Instruction> instructions = Sets.newHashSet(pseudocode.getInstructions());
146 SubroutineExitInstruction exitInstruction = pseudocode.getExitInstruction();
147 for (Instruction previousInstruction : exitInstruction.getPreviousInstructions()) {
148 previousInstruction.accept(new InstructionVisitor() {
149 @Override
150 public void visitReturnValue(@NotNull ReturnValueInstruction instruction) {
151 if (instructions.contains(instruction)) { //exclude non-local return expressions
152 returnedExpressions.add(instruction.getElement());
153 }
154 }
155
156 @Override
157 public void visitReturnNoValue(@NotNull ReturnNoValueInstruction instruction) {
158 if (instructions.contains(instruction)) {
159 returnedExpressions.add(instruction.getElement());
160 }
161 }
162
163
164 @Override
165 public void visitJump(@NotNull AbstractJumpInstruction instruction) {
166 // Nothing
167 }
168
169 @Override
170 public void visitUnconditionalJump(@NotNull UnconditionalJumpInstruction instruction) {
171 redirectToPrevInstructions(instruction);
172 }
173
174 private void redirectToPrevInstructions(Instruction instruction) {
175 for (Instruction previousInstruction : instruction.getPreviousInstructions()) {
176 previousInstruction.accept(this);
177 }
178 }
179
180 @Override
181 public void visitNondeterministicJump(@NotNull NondeterministicJumpInstruction instruction) {
182 redirectToPrevInstructions(instruction);
183 }
184
185 @Override
186 public void visitMarkInstruction(@NotNull MarkInstruction instruction) {
187 redirectToPrevInstructions(instruction);
188 }
189
190 @Override
191 public void visitInstruction(@NotNull Instruction instruction) {
192 if (instruction instanceof KtElementInstruction) {
193 KtElementInstruction elementInstruction = (KtElementInstruction) instruction;
194 returnedExpressions.add(elementInstruction.getElement());
195 }
196 else {
197 throw new IllegalStateException(instruction + " precedes the exit point");
198 }
199 }
200 });
201 }
202 }
203
204 private void checkLocalFunctions() {
205 for (LocalFunctionDeclarationInstruction localDeclarationInstruction : pseudocode.getLocalDeclarations()) {
206 KtElement element = localDeclarationInstruction.getElement();
207 if (element instanceof KtDeclarationWithBody) {
208 KtDeclarationWithBody localDeclaration = (KtDeclarationWithBody) element;
209
210 CallableDescriptor functionDescriptor =
211 (CallableDescriptor) trace.getBindingContext().get(BindingContext.DECLARATION_TO_DESCRIPTOR, localDeclaration);
212 KotlinType expectedType = functionDescriptor != null ? functionDescriptor.getReturnType() : null;
213
214 ControlFlowInformationProvider providerForLocalDeclaration =
215 new ControlFlowInformationProvider(localDeclaration, trace, localDeclarationInstruction.getBody());
216
217 providerForLocalDeclaration.checkFunction(expectedType);
218 }
219 }
220 }
221
222 public void checkDefiniteReturn(final @NotNull KotlinType expectedReturnType, @NotNull final UnreachableCode unreachableCode) {
223 assert subroutine instanceof KtDeclarationWithBody;
224 KtDeclarationWithBody function = (KtDeclarationWithBody) subroutine;
225
226 if (!function.hasBody()) return;
227
228 List<KtElement> returnedExpressions = Lists.newArrayList();
229 collectReturnExpressions(returnedExpressions);
230
231 final boolean blockBody = function.hasBlockBody();
232
233 final boolean[] noReturnError = new boolean[] { false };
234 for (KtElement returnedExpression : returnedExpressions) {
235 returnedExpression.accept(new KtVisitorVoid() {
236 @Override
237 public void visitReturnExpression(@NotNull KtReturnExpression expression) {
238 if (!blockBody) {
239 trace.report(RETURN_IN_FUNCTION_WITH_EXPRESSION_BODY.on(expression));
240 }
241 }
242
243 @Override
244 public void visitKtElement(@NotNull KtElement element) {
245 if (!(element instanceof KtExpression || element instanceof KtWhenCondition)) return;
246
247 if (blockBody && !noExpectedType(expectedReturnType)
248 && !KotlinBuiltIns.isUnit(expectedReturnType)
249 && !unreachableCode.getElements().contains(element)) {
250 noReturnError[0] = true;
251 }
252 }
253 });
254 }
255 if (noReturnError[0]) {
256 trace.report(NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY.on(function));
257 }
258 }
259
260 private void reportUnreachableCode(@NotNull UnreachableCode unreachableCode) {
261 for (KtElement element : unreachableCode.getElements()) {
262 trace.report(UNREACHABLE_CODE.on(element, unreachableCode.getUnreachableTextRanges(element)));
263 trace.record(BindingContext.UNREACHABLE_CODE, element, true);
264 }
265 }
266
267 @NotNull
268 private UnreachableCode collectUnreachableCode() {
269 Set<KtElement> reachableElements = Sets.newHashSet();
270 Set<KtElement> unreachableElements = Sets.newHashSet();
271 for (Instruction instruction : pseudocode.getInstructionsIncludingDeadCode()) {
272 if (!(instruction instanceof KtElementInstruction)
273 || instruction instanceof LoadUnitValueInstruction
274 || instruction instanceof MergeInstruction
275 || (instruction instanceof MagicInstruction && ((MagicInstruction) instruction).getSynthetic())) continue;
276
277 KtElement element = ((KtElementInstruction) instruction).getElement();
278
279 if (instruction instanceof JumpInstruction) {
280 boolean isJumpElement = element instanceof KtBreakExpression
281 || element instanceof KtContinueExpression
282 || element instanceof KtReturnExpression
283 || element instanceof KtThrowExpression;
284 if (!isJumpElement) continue;
285 }
286
287 if (instruction.getDead()) {
288 unreachableElements.add(element);
289 }
290 else {
291 reachableElements.add(element);
292 }
293 }
294 return new UnreachableCodeImpl(reachableElements, unreachableElements);
295 }
296
297 ////////////////////////////////////////////////////////////////////////////////
298 // Uninitialized variables analysis
299
300 public void markUninitializedVariables() {
301 final Collection<VariableDescriptor> varWithUninitializedErrorGenerated = Sets.newHashSet();
302 final Collection<VariableDescriptor> varWithValReassignErrorGenerated = Sets.newHashSet();
303 final boolean processClassOrObject = subroutine instanceof KtClassOrObject;
304
305 PseudocodeVariablesData pseudocodeVariablesData = getPseudocodeVariablesData();
306 Map<Instruction, Edges<Map<VariableDescriptor, VariableControlFlowState>>> initializers =
307 pseudocodeVariablesData.getVariableInitializers();
308 final Set<VariableDescriptor> declaredVariables = pseudocodeVariablesData.getDeclaredVariables(pseudocode, true);
309 final LexicalScopeVariableInfo lexicalScopeVariableInfo = pseudocodeVariablesData.getLexicalScopeVariableInfo();
310
311 final Map<Instruction, DiagnosticFactory<?>> reportedDiagnosticMap = Maps.newHashMap();
312
313 PseudocodeTraverserKt.traverse(
314 pseudocode, TraversalOrder.FORWARD, initializers,
315 new InstructionDataAnalyzeStrategy<Map<VariableDescriptor, VariableControlFlowState>>() {
316 @Override
317 public void execute(
318 @NotNull Instruction instruction,
319 @Nullable Map<VariableDescriptor, VariableControlFlowState> in,
320 @Nullable Map<VariableDescriptor, VariableControlFlowState> out
321 ) {
322 assert in != null && out != null;
323 VariableInitContext ctxt =
324 new VariableInitContext(instruction, reportedDiagnosticMap, in, out, lexicalScopeVariableInfo);
325 if (ctxt.variableDescriptor == null) return;
326 if (instruction instanceof ReadValueInstruction) {
327 ReadValueInstruction readValueInstruction = (ReadValueInstruction) instruction;
328 KtElement element = readValueInstruction.getElement();
329 if (PseudocodeUtil.isThisOrNoDispatchReceiver(readValueInstruction, trace.getBindingContext()) &&
330 declaredVariables.contains(ctxt.variableDescriptor)) {
331 checkIsInitialized(ctxt, element, varWithUninitializedErrorGenerated);
332 }
333 return;
334 }
335 if (!(instruction instanceof WriteValueInstruction)) return;
336 WriteValueInstruction writeValueInstruction = (WriteValueInstruction) instruction;
337 KtElement element = writeValueInstruction.getLValue();
338 if (!(element instanceof KtExpression)) return;
339 boolean error = checkValReassignment(ctxt, (KtExpression) element, writeValueInstruction,
340 varWithValReassignErrorGenerated);
341 if (!error && processClassOrObject) {
342 error = checkAssignmentBeforeDeclaration(ctxt, (KtExpression) element);
343 }
344 if (!error && processClassOrObject) {
345 checkInitializationForCustomSetter(ctxt, (KtExpression) element);
346 }
347 }
348 }
349 );
350 }
351
352 public void recordInitializedVariables() {
353 PseudocodeVariablesData pseudocodeVariablesData = getPseudocodeVariablesData();
354 Pseudocode pseudocode = pseudocodeVariablesData.getPseudocode();
355 Map<Instruction, Edges<Map<VariableDescriptor, VariableControlFlowState>>> initializers =
356 pseudocodeVariablesData.getVariableInitializers();
357 recordInitializedVariables(pseudocode, initializers);
358 for (LocalFunctionDeclarationInstruction instruction : pseudocode.getLocalDeclarations()) {
359 recordInitializedVariables(instruction.getBody(), initializers);
360 }
361 }
362
363 private void checkIsInitialized(
364 @NotNull VariableInitContext ctxt,
365 @NotNull KtElement element,
366 @NotNull Collection<VariableDescriptor> varWithUninitializedErrorGenerated
367 ) {
368 if (!(element instanceof KtSimpleNameExpression)) return;
369
370
371 boolean isDefinitelyInitialized = ctxt.exitInitState.definitelyInitialized();
372 VariableDescriptor variableDescriptor = ctxt.variableDescriptor;
373 if (variableDescriptor instanceof PropertyDescriptor) {
374 PropertyDescriptor propertyDescriptor = (PropertyDescriptor) variableDescriptor;
375 if (propertyDescriptor.isLateInit() || !trace.get(BindingContext.BACKING_FIELD_REQUIRED, propertyDescriptor)) {
376 isDefinitelyInitialized = true;
377 }
378 }
379 if (!isDefinitelyInitialized && !varWithUninitializedErrorGenerated.contains(variableDescriptor)) {
380 if (!(variableDescriptor instanceof PropertyDescriptor)) {
381 varWithUninitializedErrorGenerated.add(variableDescriptor);
382 }
383 if (variableDescriptor instanceof ValueParameterDescriptor) {
384 report(Errors.UNINITIALIZED_PARAMETER.on((KtSimpleNameExpression) element,
385 (ValueParameterDescriptor) variableDescriptor), ctxt);
386 }
387 else {
388 report(Errors.UNINITIALIZED_VARIABLE.on((KtSimpleNameExpression) element, variableDescriptor), ctxt);
389 }
390 }
391 }
392
393 private boolean checkValReassignment(
394 @NotNull VariableInitContext ctxt,
395 @NotNull KtExpression expression,
396 @NotNull WriteValueInstruction writeValueInstruction,
397 @NotNull Collection<VariableDescriptor> varWithValReassignErrorGenerated
398 ) {
399 VariableDescriptor variableDescriptor = ctxt.variableDescriptor;
400 PropertyDescriptor propertyDescriptor = SyntheticFieldDescriptorKt.getReferencedProperty(variableDescriptor);
401 if (KtPsiUtil.isBackingFieldReference(variableDescriptor) && propertyDescriptor != null) {
402 KtPropertyAccessor accessor = PsiTreeUtil.getParentOfType(expression, KtPropertyAccessor.class);
403 if (accessor != null) {
404 DeclarationDescriptor accessorDescriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, accessor);
405 if (propertyDescriptor.getGetter() == accessorDescriptor) {
406 //val can be reassigned through backing field inside its own getter
407 return false;
408 }
409 }
410 }
411
412 boolean mayBeInitializedNotHere = ctxt.enterInitState.mayBeInitialized();
413 boolean hasBackingField = true;
414 if (variableDescriptor instanceof PropertyDescriptor) {
415 hasBackingField = trace.get(BindingContext.BACKING_FIELD_REQUIRED, (PropertyDescriptor) variableDescriptor);
416 }
417 if (variableDescriptor.isVar() && variableDescriptor instanceof PropertyDescriptor) {
418 DeclarationDescriptor descriptor = BindingContextUtils.getEnclosingDescriptor(trace.getBindingContext(), expression);
419 PropertySetterDescriptor setterDescriptor = ((PropertyDescriptor) variableDescriptor).getSetter();
420
421 ResolvedCall<? extends CallableDescriptor> resolvedCall = CallUtilKt.getResolvedCall(expression, trace.getBindingContext());
422 ReceiverValue receiverValue = null;
423 if (resolvedCall != null) {
424 receiverValue = resolvedCall.getDispatchReceiver();
425 }
426
427 if (Visibilities.isVisible(receiverValue, variableDescriptor, descriptor) && setterDescriptor != null
428 && !Visibilities.isVisible(receiverValue, setterDescriptor, descriptor)) {
429 report(Errors.INVISIBLE_SETTER.on(expression, variableDescriptor, setterDescriptor.getVisibility(),
430 setterDescriptor), ctxt);
431 return true;
432 }
433 }
434 boolean isThisOrNoDispatchReceiver =
435 PseudocodeUtil.isThisOrNoDispatchReceiver(writeValueInstruction, trace.getBindingContext());
436 if ((mayBeInitializedNotHere || !hasBackingField || !isThisOrNoDispatchReceiver) && !variableDescriptor.isVar()) {
437 boolean hasReassignMethodReturningUnit = false;
438 KtSimpleNameExpression operationReference = null;
439 PsiElement parent = expression.getParent();
440 if (parent instanceof KtBinaryExpression) {
441 operationReference = ((KtBinaryExpression) parent).getOperationReference();
442 }
443 else if (parent instanceof KtUnaryExpression) {
444 operationReference = ((KtUnaryExpression) parent).getOperationReference();
445 }
446 if (operationReference != null) {
447 DeclarationDescriptor descriptor = trace.get(BindingContext.REFERENCE_TARGET, operationReference);
448 if (descriptor instanceof FunctionDescriptor) {
449 if (KotlinBuiltIns.isUnit(((FunctionDescriptor) descriptor).getReturnType())) {
450 hasReassignMethodReturningUnit = true;
451 }
452 }
453 if (descriptor == null) {
454 Collection<? extends DeclarationDescriptor> descriptors =
455 trace.get(BindingContext.AMBIGUOUS_REFERENCE_TARGET, operationReference);
456 if (descriptors != null) {
457 for (DeclarationDescriptor referenceDescriptor : descriptors) {
458 if (KotlinBuiltIns.isUnit(((FunctionDescriptor) referenceDescriptor).getReturnType())) {
459 hasReassignMethodReturningUnit = true;
460 }
461 }
462 }
463 }
464 }
465 if (!hasReassignMethodReturningUnit) {
466 if (!isThisOrNoDispatchReceiver || !varWithValReassignErrorGenerated.contains(variableDescriptor)) {
467 report(Errors.VAL_REASSIGNMENT.on(expression, variableDescriptor), ctxt);
468 }
469 if (isThisOrNoDispatchReceiver) {
470 // try to get rid of repeating VAL_REASSIGNMENT diagnostic only for vars with no receiver
471 // or when receiver is this
472 varWithValReassignErrorGenerated.add(variableDescriptor);
473 }
474 return true;
475 }
476 }
477 return false;
478 }
479
480 private boolean checkAssignmentBeforeDeclaration(@NotNull VariableInitContext ctxt, @NotNull KtExpression expression) {
481 if (!ctxt.enterInitState.isDeclared && !ctxt.exitInitState.isDeclared
482 && !ctxt.enterInitState.mayBeInitialized() && ctxt.exitInitState.mayBeInitialized()) {
483 report(Errors.INITIALIZATION_BEFORE_DECLARATION.on(expression, ctxt.variableDescriptor), ctxt);
484 return true;
485 }
486 return false;
487 }
488
489 private boolean checkInitializationForCustomSetter(@NotNull VariableInitContext ctxt, @NotNull KtExpression expression) {
490 VariableDescriptor variableDescriptor = ctxt.variableDescriptor;
491 if (!(variableDescriptor instanceof PropertyDescriptor)
492 || ctxt.enterInitState.mayBeInitialized()
493 || !ctxt.exitInitState.mayBeInitialized()
494 || !variableDescriptor.isVar()
495 || !trace.get(BindingContext.BACKING_FIELD_REQUIRED, (PropertyDescriptor) variableDescriptor)
496 ) {
497 return false;
498 }
499
500 PsiElement property = DescriptorToSourceUtils.descriptorToDeclaration(variableDescriptor);
501 assert property instanceof KtProperty;
502 KtPropertyAccessor setter = ((KtProperty) property).getSetter();
503 if (((PropertyDescriptor) variableDescriptor).getModality() == Modality.FINAL && (setter == null || !setter.hasBody())) {
504 return false;
505 }
506
507 KtExpression variable = expression;
508 if (expression instanceof KtDotQualifiedExpression) {
509 if (((KtDotQualifiedExpression) expression).getReceiverExpression() instanceof KtThisExpression) {
510 variable = ((KtDotQualifiedExpression) expression).getSelectorExpression();
511 }
512 }
513 if (variable instanceof KtSimpleNameExpression) {
514 trace.record(IS_UNINITIALIZED, (PropertyDescriptor) variableDescriptor);
515 return true;
516 }
517 return false;
518 }
519
520 private void recordInitializedVariables(
521 @NotNull Pseudocode pseudocode,
522 @NotNull Map<Instruction, Edges<Map<VariableDescriptor, VariableControlFlowState>>> initializersMap
523 ) {
524 Edges<Map<VariableDescriptor, VariableControlFlowState>> initializers = initializersMap.get(pseudocode.getExitInstruction());
525 if (initializers == null) return;
526 Set<VariableDescriptor> declaredVariables = getPseudocodeVariablesData().getDeclaredVariables(pseudocode, false);
527 for (VariableDescriptor variable : declaredVariables) {
528 if (variable instanceof PropertyDescriptor) {
529 VariableControlFlowState variableControlFlowState = initializers.getIncoming().get(variable);
530 if (variableControlFlowState != null && variableControlFlowState.definitelyInitialized()) continue;
531 trace.record(BindingContext.IS_UNINITIALIZED, (PropertyDescriptor) variable);
532 }
533 }
534 }
535
536 ////////////////////////////////////////////////////////////////////////////////
537 // "Unused variable" & "unused value" analyses
538
539 public void markUnusedVariables() {
540 final PseudocodeVariablesData pseudocodeVariablesData = getPseudocodeVariablesData();
541 Map<Instruction, Edges<Map<VariableDescriptor, VariableUseState>>> variableStatusData =
542 pseudocodeVariablesData.getVariableUseStatusData();
543 final Map<Instruction, DiagnosticFactory<?>> reportedDiagnosticMap = Maps.newHashMap();
544 InstructionDataAnalyzeStrategy<Map<VariableDescriptor, VariableUseState>> variableStatusAnalyzeStrategy =
545 new InstructionDataAnalyzeStrategy<Map<VariableDescriptor, VariableUseState>>() {
546 @Override
547 public void execute(
548 @NotNull Instruction instruction,
549 @Nullable Map<VariableDescriptor, VariableUseState> in,
550 @Nullable Map<VariableDescriptor, VariableUseState> out
551 ) {
552
553 assert in != null && out != null;
554 VariableContext ctxt = new VariableUseContext(instruction, reportedDiagnosticMap, in, out);
555 Set<VariableDescriptor> declaredVariables =
556 pseudocodeVariablesData.getDeclaredVariables(instruction.getOwner(), false);
557 VariableDescriptor variableDescriptor = PseudocodeUtil.extractVariableDescriptorIfAny(
558 instruction, false, trace.getBindingContext());
559 if (variableDescriptor == null || !declaredVariables.contains(variableDescriptor)
560 || !ExpressionTypingUtils.isLocal(variableDescriptor.getContainingDeclaration(), variableDescriptor)) {
561 return;
562 }
563 PseudocodeVariablesData.VariableUseState variableUseState = in.get(variableDescriptor);
564 if (instruction instanceof WriteValueInstruction) {
565 if (trace.get(CAPTURED_IN_CLOSURE, variableDescriptor) != null) return;
566 KtElement element = ((WriteValueInstruction) instruction).getElement();
567 if (variableUseState != READ) {
568 if (element instanceof KtBinaryExpression &&
569 ((KtBinaryExpression) element).getOperationToken() == KtTokens.EQ) {
570 KtExpression right = ((KtBinaryExpression) element).getRight();
571 if (right != null) {
572 report(Errors.UNUSED_VALUE.on((KtBinaryExpression) element, right, variableDescriptor), ctxt);
573 }
574 }
575 else if (element instanceof KtPostfixExpression) {
576 IElementType operationToken =
577 ((KtPostfixExpression) element).getOperationReference().getReferencedNameElementType();
578 if (operationToken == KtTokens.PLUSPLUS || operationToken == KtTokens.MINUSMINUS) {
579 report(Errors.UNUSED_CHANGED_VALUE.on(element, element), ctxt);
580 }
581 }
582 }
583 }
584 else if (instruction instanceof VariableDeclarationInstruction) {
585 KtDeclaration element = ((VariableDeclarationInstruction) instruction).getVariableDeclarationElement();
586 if (!(element instanceof KtNamedDeclaration)) return;
587 PsiElement nameIdentifier = ((KtNamedDeclaration) element).getNameIdentifier();
588 if (nameIdentifier == null) return;
589 if (!VariableUseState.isUsed(variableUseState)) {
590 if (KtPsiUtil.isVariableNotParameterDeclaration(element)) {
591 report(Errors.UNUSED_VARIABLE.on((KtNamedDeclaration) element, variableDescriptor), ctxt);
592 }
593 else if (element instanceof KtParameter) {
594 PsiElement owner = element.getParent().getParent();
595 if (owner instanceof KtPrimaryConstructor) {
596 if (!((KtParameter) element).hasValOrVar()) {
597 KtClassOrObject containingClass = ((KtPrimaryConstructor) owner).getContainingClassOrObject();
598 DeclarationDescriptor containingClassDescriptor = trace.get(
599 BindingContext.DECLARATION_TO_DESCRIPTOR, containingClass
600 );
601 if (!DescriptorUtils.isAnnotationClass(containingClassDescriptor)) {
602 report(Errors.UNUSED_PARAMETER.on((KtParameter) element, variableDescriptor), ctxt);
603 }
604 }
605 }
606 else if (owner instanceof KtFunction) {
607 MainFunctionDetector mainFunctionDetector = new MainFunctionDetector(trace.getBindingContext());
608 boolean isMain = (owner instanceof KtNamedFunction) && mainFunctionDetector.isMain((KtNamedFunction) owner);
609 if (owner instanceof KtFunctionLiteral) return;
610 DeclarationDescriptor descriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, owner);
611 assert descriptor instanceof FunctionDescriptor : owner.getText();
612 FunctionDescriptor functionDescriptor = (FunctionDescriptor) descriptor;
613 String functionName = functionDescriptor.getName().asString();
614 KtFunction function = (KtFunction) owner;
615 if (isMain
616 || ModalityKt.isOverridableOrOverrides(functionDescriptor)
617 || function.hasModifier(KtTokens.OVERRIDE_KEYWORD)
618 || "getValue".equals(functionName) || "setValue".equals(functionName)
619 || "propertyDelegated".equals(functionName)
620 ) {
621 return;
622 }
623 report(Errors.UNUSED_PARAMETER.on((KtParameter) element, variableDescriptor), ctxt);
624 }
625 }
626 }
627 else if (variableUseState == ONLY_WRITTEN_NEVER_READ && KtPsiUtil.isVariableNotParameterDeclaration(element)) {
628 report(Errors.ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE.on((KtNamedDeclaration) element, variableDescriptor), ctxt);
629 }
630 else if (variableUseState == WRITTEN_AFTER_READ && element instanceof KtVariableDeclaration) {
631 if (element instanceof KtProperty) {
632 KtExpression initializer = ((KtProperty) element).getInitializer();
633 if (initializer != null) {
634 report(Errors.VARIABLE_WITH_REDUNDANT_INITIALIZER.on(initializer, variableDescriptor), ctxt);
635 }
636 }
637 else if (element instanceof KtDestructuringDeclarationEntry) {
638 report(VARIABLE_WITH_REDUNDANT_INITIALIZER.on(element, variableDescriptor), ctxt);
639 }
640 }
641 }
642 }
643 };
644 PseudocodeTraverserKt.traverse(pseudocode, TraversalOrder.BACKWARD, variableStatusData, variableStatusAnalyzeStrategy);
645 }
646
647 ////////////////////////////////////////////////////////////////////////////////
648 // "Unused expressions" in block
649
650 public void markUnusedExpressions() {
651 final Map<Instruction, DiagnosticFactory<?>> reportedDiagnosticMap = Maps.newHashMap();
652 PseudocodeTraverserKt.traverse(
653 pseudocode, TraversalOrder.FORWARD, new ControlFlowInformationProvider.FunctionVoid1<Instruction>() {
654 @Override
655 public void execute(@NotNull Instruction instruction) {
656 if (!(instruction instanceof KtElementInstruction)) return;
657
658 KtElement element = ((KtElementInstruction)instruction).getElement();
659 if (!(element instanceof KtExpression)) return;
660
661 if (BindingContextUtilsKt.isUsedAsStatement((KtExpression) element, trace.getBindingContext())
662 && PseudocodeUtilsKt.getSideEffectFree(instruction)) {
663 VariableContext ctxt = new VariableContext(instruction, reportedDiagnosticMap);
664 report(
665 element instanceof KtLambdaExpression
666 ? Errors.UNUSED_LAMBDA_EXPRESSION.on((KtLambdaExpression) element)
667 : Errors.UNUSED_EXPRESSION.on(element),
668 ctxt
669 );
670 }
671 }
672 }
673 );
674 }
675
676 ////////////////////////////////////////////////////////////////////////////////
677 // Statements
678
679 public void markStatements() {
680 PseudocodeTraverserKt.traverse(
681 pseudocode, TraversalOrder.FORWARD, new ControlFlowInformationProvider.FunctionVoid1<Instruction>() {
682 @Override
683 public void execute(@NotNull Instruction instruction) {
684 PseudoValue value = instruction instanceof InstructionWithValue
685 ? ((InstructionWithValue) instruction).getOutputValue()
686 : null;
687 Pseudocode pseudocode = instruction.getOwner();
688 boolean isUsedAsExpression = !pseudocode.getUsages(value).isEmpty();
689 for (KtElement element : pseudocode.getValueElements(value)) {
690 trace.record(BindingContext.USED_AS_EXPRESSION, element, isUsedAsExpression);
691 }
692 }
693 }
694 );
695 }
696
697 public void markIfWithoutElse() {
698 PseudocodeTraverserKt.traverse(
699 pseudocode, TraversalOrder.FORWARD, new ControlFlowInformationProvider.FunctionVoid1<Instruction>() {
700 @Override
701 public void execute(@NotNull Instruction instruction) {
702 PseudoValue value = instruction instanceof InstructionWithValue
703 ? ((InstructionWithValue) instruction).getOutputValue()
704 : null;
705 for (KtElement element : instruction.getOwner().getValueElements(value)) {
706 if (!(element instanceof KtIfExpression)) continue;
707 KtIfExpression ifExpression = (KtIfExpression) element;
708 if (ifExpression.getThen() != null && ifExpression.getElse() != null) continue;
709
710 if (BindingContextUtilsKt.isUsedAsExpression(ifExpression, trace.getBindingContext())) {
711 trace.report(INVALID_IF_AS_EXPRESSION.on(ifExpression));
712 }
713 }
714 }
715 }
716 );
717 }
718
719 public void markWhenWithoutElse() {
720 PseudocodeTraverserKt.traverse(
721 pseudocode, TraversalOrder.FORWARD, new ControlFlowInformationProvider.FunctionVoid1<Instruction>() {
722 @Override
723 public void execute(@NotNull Instruction instruction) {
724 PseudoValue value = instruction instanceof InstructionWithValue
725 ? ((InstructionWithValue) instruction).getOutputValue()
726 : null;
727 for (KtElement element : instruction.getOwner().getValueElements(value)) {
728 if (!(element instanceof KtWhenExpression)) continue;
729 KtWhenExpression whenExpression = (KtWhenExpression) element;
730 if (whenExpression.getElseExpression() != null) continue;
731
732 if (WhenChecker.mustHaveElse(whenExpression, trace)) {
733 trace.report(NO_ELSE_IN_WHEN.on(whenExpression));
734 }
735 else if (whenExpression.getSubjectExpression() != null) {
736 ClassDescriptor enumClassDescriptor = WhenChecker.getClassDescriptorOfTypeIfEnum(
737 trace.getType(whenExpression.getSubjectExpression()));
738 if (enumClassDescriptor != null
739 && !WhenChecker.isWhenOnEnumExhaustive(whenExpression, trace, enumClassDescriptor)) {
740 trace.report(NON_EXHAUSTIVE_WHEN.on(whenExpression));
741 }
742 }
743 }
744 }
745 }
746 );
747 }
748
749 ////////////////////////////////////////////////////////////////////////////////
750 // Tail calls
751
752 public void markTailCalls() {
753 final DeclarationDescriptor subroutineDescriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, subroutine);
754 if (!(subroutineDescriptor instanceof FunctionDescriptor)) return;
755 if (!((FunctionDescriptor) subroutineDescriptor).isTailrec()) return;
756
757 // finally blocks are copied which leads to multiple diagnostics reported on one instruction
758 class KindAndCall {
759 TailRecursionKind kind;
760 ResolvedCall<?> call;
761
762 KindAndCall(TailRecursionKind kind, ResolvedCall<?> call) {
763 this.kind = kind;
764 this.call = call;
765 }
766 }
767 final Map<KtElement, KindAndCall> calls = new HashMap<KtElement, KindAndCall>();
768 PseudocodeTraverserKt.traverse(
769 pseudocode,
770 TraversalOrder.FORWARD,
771 new FunctionVoid1<Instruction>() {
772 public void execute(@NotNull Instruction instruction) {
773 if (!(instruction instanceof CallInstruction)) return;
774 CallInstruction callInstruction = (CallInstruction) instruction;
775
776 ResolvedCall<?> resolvedCall = CallUtilKt.getResolvedCall(callInstruction.getElement(), trace.getBindingContext());
777 if (resolvedCall == null) return;
778
779 // is this a recursive call?
780 CallableDescriptor functionDescriptor = resolvedCall.getResultingDescriptor();
781 if (!functionDescriptor.getOriginal().equals(subroutineDescriptor)) return;
782
783 KtElement element = callInstruction.getElement();
784 //noinspection unchecked
785 KtExpression parent = PsiTreeUtil.getParentOfType(
786 element,
787 KtTryExpression.class, KtFunction.class, KtAnonymousInitializer.class
788 );
789
790 if (parent instanceof KtTryExpression) {
791 // We do not support tail calls Collections.singletonMap() try-catch-finally, for simplicity of the mental model
792 // very few cases there would be real tail-calls, and it's often not so easy for the user to see why
793 calls.put(element, new KindAndCall(IN_TRY, resolvedCall));
794 return;
795 }
796
797 boolean isTail = PseudocodeTraverserKt.traverseFollowingInstructions(
798 callInstruction,
799 new HashSet<Instruction>(),
800 TraversalOrder.FORWARD,
801 new TailRecursionDetector(subroutine, callInstruction)
802 );
803
804 // A tail call is not allowed to change dispatch receiver
805 // class C {
806 // fun foo(other: C) {
807 // other.foo(this) // not a tail call
808 // }
809 // }
810 boolean sameDispatchReceiver =
811 ResolvedCallUtilKt.hasThisOrNoDispatchReceiver(resolvedCall, trace.getBindingContext());
812
813 TailRecursionKind kind = isTail && sameDispatchReceiver ? TAIL_CALL : NON_TAIL;
814
815 KindAndCall kindAndCall = calls.get(element);
816 calls.put(element,
817 new KindAndCall(
818 combineKinds(kind, kindAndCall == null ? null : kindAndCall.kind),
819 resolvedCall
820 )
821 );
822 }
823 }
824 );
825 boolean hasTailCalls = false;
826 for (Map.Entry<KtElement, KindAndCall> entry : calls.entrySet()) {
827 KtElement element = entry.getKey();
828 KindAndCall kindAndCall = entry.getValue();
829 switch (kindAndCall.kind) {
830 case TAIL_CALL:
831 trace.record(TAIL_RECURSION_CALL, kindAndCall.call, TailRecursionKind.TAIL_CALL);
832 hasTailCalls = true;
833 break;
834 case IN_TRY:
835 trace.report(Errors.TAIL_RECURSION_IN_TRY_IS_NOT_SUPPORTED.on(element));
836 break;
837 case NON_TAIL:
838 trace.report(Errors.NON_TAIL_RECURSIVE_CALL.on(element));
839 break;
840 }
841 }
842
843 if (!hasTailCalls) {
844 trace.report(Errors.NO_TAIL_CALLS_FOUND.on((KtNamedFunction) subroutine));
845 }
846 }
847
848 private static TailRecursionKind combineKinds(TailRecursionKind kind, @Nullable TailRecursionKind existingKind) {
849 TailRecursionKind resultingKind;
850 if (existingKind == null || existingKind == kind) {
851 resultingKind = kind;
852 }
853 else {
854 if (check(kind, existingKind, IN_TRY, TAIL_CALL)) {
855 resultingKind = IN_TRY;
856 }
857 else if (check(kind, existingKind, IN_TRY, NON_TAIL)) {
858 resultingKind = IN_TRY;
859 }
860 else {
861 // TAIL_CALL, NON_TAIL
862 resultingKind = NON_TAIL;
863 }
864 }
865 return resultingKind;
866 }
867
868 private static boolean check(Object a, Object b, Object x, Object y) {
869 return (a == x && b == y) || (a == y && b == x);
870 }
871
872 ////////////////////////////////////////////////////////////////////////////////
873 // Utility classes and methods
874
875 /**
876 * The method provides reporting of the same diagnostic only once for copied instructions
877 * (depends on whether it should be reported for all or only for one of the copies)
878 */
879 private void report(
880 @NotNull Diagnostic diagnostic,
881 @NotNull VariableContext ctxt
882 ) {
883 Instruction instruction = ctxt.instruction;
884 if (instruction.getCopies().isEmpty()) {
885 trace.report(diagnostic);
886 return;
887 }
888 Map<Instruction, DiagnosticFactory<?>> previouslyReported = ctxt.reportedDiagnosticMap;
889 previouslyReported.put(instruction, diagnostic.getFactory());
890
891 boolean alreadyReported = false;
892 boolean sameErrorForAllCopies = true;
893 for (Instruction copy : instruction.getCopies()) {
894 DiagnosticFactory<?> previouslyReportedErrorFactory = previouslyReported.get(copy);
895 if (previouslyReportedErrorFactory != null) {
896 alreadyReported = true;
897 }
898
899 if (previouslyReportedErrorFactory != diagnostic.getFactory()) {
900 sameErrorForAllCopies = false;
901 }
902 }
903
904 if (mustBeReportedOnAllCopies(diagnostic.getFactory())) {
905 if (sameErrorForAllCopies) {
906 trace.report(diagnostic);
907 }
908 }
909 else {
910 //only one reporting required
911 if (!alreadyReported) {
912 trace.report(diagnostic);
913 }
914 }
915 }
916
917 private static boolean mustBeReportedOnAllCopies(@NotNull DiagnosticFactory<?> diagnosticFactory) {
918 return diagnosticFactory == UNUSED_VARIABLE
919 || diagnosticFactory == UNUSED_PARAMETER
920 || diagnosticFactory == UNUSED_CHANGED_VALUE;
921 }
922
923
924 private class VariableContext {
925 final Map<Instruction, DiagnosticFactory<?>> reportedDiagnosticMap;
926 final Instruction instruction;
927 final VariableDescriptor variableDescriptor;
928
929 private VariableContext(
930 @NotNull Instruction instruction,
931 @NotNull Map<Instruction, DiagnosticFactory<?>> map
932 ) {
933 this.instruction = instruction;
934 reportedDiagnosticMap = map;
935 variableDescriptor = PseudocodeUtil.extractVariableDescriptorIfAny(instruction, true, trace.getBindingContext());
936 }
937 }
938
939 private class VariableInitContext extends VariableContext {
940 final VariableControlFlowState enterInitState;
941 final VariableControlFlowState exitInitState;
942
943 private VariableInitContext(
944 @NotNull Instruction instruction,
945 @NotNull Map<Instruction, DiagnosticFactory<?>> map,
946 @NotNull Map<VariableDescriptor, VariableControlFlowState> in,
947 @NotNull Map<VariableDescriptor, VariableControlFlowState> out,
948 @NotNull LexicalScopeVariableInfo lexicalScopeVariableInfo
949 ) {
950 super(instruction, map);
951 enterInitState = initialize(variableDescriptor, lexicalScopeVariableInfo, in);
952 exitInitState = initialize(variableDescriptor, lexicalScopeVariableInfo, out);
953 }
954
955 private VariableControlFlowState initialize(
956 VariableDescriptor variableDescriptor,
957 LexicalScopeVariableInfo lexicalScopeVariableInfo,
958 Map<VariableDescriptor, VariableControlFlowState> map
959 ) {
960 if (variableDescriptor == null) return null;
961 VariableControlFlowState state = map.get(variableDescriptor);
962 if (state != null) return state;
963 return PseudocodeVariablesData.getDefaultValueForInitializers(variableDescriptor, instruction, lexicalScopeVariableInfo);
964 }
965 }
966
967 private class VariableUseContext extends VariableContext {
968 final VariableUseState enterUseState;
969 final VariableUseState exitUseState;
970
971
972 private VariableUseContext(
973 @NotNull Instruction instruction,
974 @NotNull Map<Instruction, DiagnosticFactory<?>> map,
975 @NotNull Map<VariableDescriptor, VariableUseState> in,
976 @NotNull Map<VariableDescriptor, VariableUseState> out
977 ) {
978 super(instruction, map);
979 enterUseState = variableDescriptor != null ? in.get(variableDescriptor) : null;
980 exitUseState = variableDescriptor != null ? out.get(variableDescriptor) : null;
981 }
982 }
983
984 //TODO after KT-4621 rewrite to Kotlin
985 public abstract static class InstructionDataAnalyzeStrategy<D> implements Function3<Instruction, D, D, Unit> {
986 @Override
987 public Unit invoke(Instruction instruction, D enterData, D exitData) {
988 execute(instruction, enterData, exitData);
989 return Unit.INSTANCE$;
990 }
991
992 public abstract void execute(Instruction instruction, D enterData, D exitData);
993 }
994
995 public abstract static class FunctionVoid1<P> implements Function1<P, Unit> {
996 @Override
997 public Unit invoke(P p) {
998 execute(p);
999 return Unit.INSTANCE$;
1000 }
1001
1002 public abstract void execute(P p);
1003 }
1004 }