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.pseudocode.PseudoValue;
032 import org.jetbrains.kotlin.cfg.pseudocode.Pseudocode;
033 import org.jetbrains.kotlin.cfg.pseudocode.PseudocodeUtil;
034 import org.jetbrains.kotlin.cfg.pseudocode.PseudocodeUtilsKt;
035 import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction;
036 import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionVisitor;
037 import org.jetbrains.kotlin.cfg.pseudocode.instructions.KtElementInstruction;
038 import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.*;
039 import org.jetbrains.kotlin.cfg.pseudocode.instructions.jumps.*;
040 import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.LocalFunctionDeclarationInstruction;
041 import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.MarkInstruction;
042 import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.SubroutineExitInstruction;
043 import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.VariableDeclarationInstruction;
044 import org.jetbrains.kotlin.cfg.pseudocodeTraverser.Edges;
045 import org.jetbrains.kotlin.cfg.pseudocodeTraverser.PseudocodeTraverserKt;
046 import org.jetbrains.kotlin.cfg.pseudocodeTraverser.TraversalOrder;
047 import org.jetbrains.kotlin.descriptors.*;
048 import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptorKt;
049 import org.jetbrains.kotlin.diagnostics.Diagnostic;
050 import org.jetbrains.kotlin.diagnostics.DiagnosticFactory;
051 import org.jetbrains.kotlin.diagnostics.Errors;
052 import org.jetbrains.kotlin.idea.MainFunctionDetector;
053 import org.jetbrains.kotlin.lexer.KtTokens;
054 import org.jetbrains.kotlin.psi.*;
055 import org.jetbrains.kotlin.resolve.*;
056 import org.jetbrains.kotlin.resolve.bindingContextUtil.BindingContextUtilsKt;
057 import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt;
058 import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
059 import org.jetbrains.kotlin.resolve.calls.resolvedCallUtil.ResolvedCallUtilKt;
060 import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
061 import org.jetbrains.kotlin.types.KotlinType;
062 import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils;
063
064 import java.util.*;
065
066 import static org.jetbrains.kotlin.cfg.TailRecursionKind.*;
067 import static org.jetbrains.kotlin.cfg.VariableUseState.*;
068 import static org.jetbrains.kotlin.diagnostics.Errors.*;
069 import static org.jetbrains.kotlin.diagnostics.Errors.UNREACHABLE_CODE;
070 import static org.jetbrains.kotlin.resolve.BindingContext.*;
071 import static org.jetbrains.kotlin.types.TypeUtils.*;
072
073 public class ControlFlowInformationProvider {
074
075 private final KtElement subroutine;
076 private final Pseudocode pseudocode;
077 private final BindingTrace trace;
078 private PseudocodeVariablesData pseudocodeVariablesData;
079
080 private ControlFlowInformationProvider(
081 @NotNull KtElement declaration,
082 @NotNull BindingTrace trace,
083 @NotNull Pseudocode pseudocode
084 ) {
085 this.subroutine = declaration;
086 this.trace = trace;
087 this.pseudocode = pseudocode;
088 }
089
090 public ControlFlowInformationProvider(
091 @NotNull KtElement declaration,
092 @NotNull BindingTrace trace
093 ) {
094 this(declaration, trace, new ControlFlowProcessor(trace).generatePseudocode(declaration));
095 }
096
097 public PseudocodeVariablesData getPseudocodeVariablesData() {
098 if (pseudocodeVariablesData == null) {
099 pseudocodeVariablesData = new PseudocodeVariablesData(pseudocode, trace.getBindingContext());
100 }
101 return pseudocodeVariablesData;
102 }
103
104 public void checkForLocalClassOrObjectMode() {
105 // Local classes and objects are analyzed twice: when TopDownAnalyzer processes it and as a part of its container.
106 // Almost all checks can be done when the container is analyzed
107 // except recording initialized variables (this information is needed for DeclarationChecker).
108 recordInitializedVariables();
109 }
110
111 public void checkDeclaration() {
112
113 recordInitializedVariables();
114
115 checkLocalFunctions();
116
117 markUninitializedVariables();
118
119 markUnusedVariables();
120
121 markStatements();
122
123 markUnusedExpressions();
124
125 checkIfExpressions();
126
127 checkWhenExpressions();
128 }
129
130 public void checkFunction(@Nullable KotlinType expectedReturnType) {
131 UnreachableCode unreachableCode = collectUnreachableCode();
132 reportUnreachableCode(unreachableCode);
133
134 if (subroutine instanceof KtFunctionLiteral) return;
135
136 checkDefiniteReturn(expectedReturnType != null ? expectedReturnType : NO_EXPECTED_TYPE, unreachableCode);
137
138 markTailCalls();
139 }
140
141 private void collectReturnExpressions(@NotNull final Collection<KtElement> returnedExpressions) {
142 final Set<Instruction> instructions = Sets.newHashSet(pseudocode.getInstructions());
143 SubroutineExitInstruction exitInstruction = pseudocode.getExitInstruction();
144 for (Instruction previousInstruction : exitInstruction.getPreviousInstructions()) {
145 previousInstruction.accept(new InstructionVisitor() {
146 @Override
147 public void visitReturnValue(@NotNull ReturnValueInstruction instruction) {
148 if (instructions.contains(instruction)) { //exclude non-local return expressions
149 returnedExpressions.add(instruction.getElement());
150 }
151 }
152
153 @Override
154 public void visitReturnNoValue(@NotNull ReturnNoValueInstruction instruction) {
155 if (instructions.contains(instruction)) {
156 returnedExpressions.add(instruction.getElement());
157 }
158 }
159
160
161 @Override
162 public void visitJump(@NotNull AbstractJumpInstruction instruction) {
163 // Nothing
164 }
165
166 @Override
167 public void visitUnconditionalJump(@NotNull UnconditionalJumpInstruction instruction) {
168 redirectToPrevInstructions(instruction);
169 }
170
171 private void redirectToPrevInstructions(Instruction instruction) {
172 for (Instruction previousInstruction : instruction.getPreviousInstructions()) {
173 previousInstruction.accept(this);
174 }
175 }
176
177 @Override
178 public void visitNondeterministicJump(@NotNull NondeterministicJumpInstruction instruction) {
179 redirectToPrevInstructions(instruction);
180 }
181
182 @Override
183 public void visitMarkInstruction(@NotNull MarkInstruction instruction) {
184 redirectToPrevInstructions(instruction);
185 }
186
187 @Override
188 public void visitInstruction(@NotNull Instruction instruction) {
189 if (instruction instanceof KtElementInstruction) {
190 KtElementInstruction elementInstruction = (KtElementInstruction) instruction;
191 returnedExpressions.add(elementInstruction.getElement());
192 }
193 else {
194 throw new IllegalStateException(instruction + " precedes the exit point");
195 }
196 }
197 });
198 }
199 }
200
201 private void checkLocalFunctions() {
202 for (LocalFunctionDeclarationInstruction localDeclarationInstruction : pseudocode.getLocalDeclarations()) {
203 KtElement element = localDeclarationInstruction.getElement();
204 if (element instanceof KtDeclarationWithBody) {
205 KtDeclarationWithBody localDeclaration = (KtDeclarationWithBody) element;
206
207 CallableDescriptor functionDescriptor =
208 (CallableDescriptor) trace.getBindingContext().get(BindingContext.DECLARATION_TO_DESCRIPTOR, localDeclaration);
209 KotlinType expectedType = functionDescriptor != null ? functionDescriptor.getReturnType() : null;
210
211 ControlFlowInformationProvider providerForLocalDeclaration =
212 new ControlFlowInformationProvider(localDeclaration, trace, localDeclarationInstruction.getBody());
213
214 providerForLocalDeclaration.checkFunction(expectedType);
215 }
216 }
217 }
218
219 public void checkDefiniteReturn(final @NotNull KotlinType expectedReturnType, @NotNull final UnreachableCode unreachableCode) {
220 assert subroutine instanceof KtDeclarationWithBody;
221 KtDeclarationWithBody function = (KtDeclarationWithBody) subroutine;
222
223 if (!function.hasBody()) return;
224
225 List<KtElement> returnedExpressions = Lists.newArrayList();
226 collectReturnExpressions(returnedExpressions);
227
228 final boolean blockBody = function.hasBlockBody();
229
230 final boolean[] noReturnError = new boolean[] { false };
231 for (KtElement returnedExpression : returnedExpressions) {
232 returnedExpression.accept(new KtVisitorVoid() {
233 @Override
234 public void visitReturnExpression(@NotNull KtReturnExpression expression) {
235 if (!blockBody) {
236 trace.report(RETURN_IN_FUNCTION_WITH_EXPRESSION_BODY.on(expression));
237 }
238 }
239
240 @Override
241 public void visitKtElement(@NotNull KtElement element) {
242 if (!(element instanceof KtExpression || element instanceof KtWhenCondition)) return;
243
244 if (blockBody && !noExpectedType(expectedReturnType)
245 && !KotlinBuiltIns.isUnit(expectedReturnType)
246 && !unreachableCode.getElements().contains(element)) {
247 noReturnError[0] = true;
248 }
249 }
250 });
251 }
252 if (noReturnError[0]) {
253 trace.report(NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY.on(function));
254 }
255 }
256
257 private void reportUnreachableCode(@NotNull UnreachableCode unreachableCode) {
258 for (KtElement element : unreachableCode.getElements()) {
259 trace.report(UNREACHABLE_CODE.on(element, unreachableCode.getUnreachableTextRanges(element)));
260 trace.record(BindingContext.UNREACHABLE_CODE, element, true);
261 }
262 }
263
264 @NotNull
265 private UnreachableCode collectUnreachableCode() {
266 Set<KtElement> reachableElements = Sets.newHashSet();
267 Set<KtElement> unreachableElements = Sets.newHashSet();
268 for (Instruction instruction : pseudocode.getInstructionsIncludingDeadCode()) {
269 if (!(instruction instanceof KtElementInstruction)
270 || instruction instanceof LoadUnitValueInstruction
271 || instruction instanceof MergeInstruction
272 || (instruction instanceof MagicInstruction && ((MagicInstruction) instruction).getSynthetic())) continue;
273
274 KtElement element = ((KtElementInstruction) instruction).getElement();
275
276 if (instruction instanceof JumpInstruction) {
277 boolean isJumpElement = element instanceof KtBreakExpression
278 || element instanceof KtContinueExpression
279 || element instanceof KtReturnExpression
280 || element instanceof KtThrowExpression;
281 if (!isJumpElement) continue;
282 }
283
284 if (instruction.getDead()) {
285 unreachableElements.add(element);
286 }
287 else {
288 reachableElements.add(element);
289 }
290 }
291 return new UnreachableCodeImpl(reachableElements, unreachableElements);
292 }
293
294 ////////////////////////////////////////////////////////////////////////////////
295 // Uninitialized variables analysis
296
297 public void markUninitializedVariables() {
298 final Collection<VariableDescriptor> varWithUninitializedErrorGenerated = Sets.newHashSet();
299 final Collection<VariableDescriptor> varWithValReassignErrorGenerated = Sets.newHashSet();
300 final boolean processClassOrObject = subroutine instanceof KtClassOrObject;
301
302 PseudocodeVariablesData pseudocodeVariablesData = getPseudocodeVariablesData();
303 Map<Instruction, Edges<InitControlFlowInfo>> initializers =
304 pseudocodeVariablesData.getVariableInitializers();
305 final Set<VariableDescriptor> declaredVariables = pseudocodeVariablesData.getDeclaredVariables(pseudocode, true);
306 final LexicalScopeVariableInfo lexicalScopeVariableInfo = pseudocodeVariablesData.getLexicalScopeVariableInfo();
307
308 final Map<Instruction, DiagnosticFactory<?>> reportedDiagnosticMap = Maps.newHashMap();
309
310 PseudocodeTraverserKt.traverse(
311 pseudocode, TraversalOrder.FORWARD, initializers,
312 new InstructionDataAnalyzeStrategy<Map<VariableDescriptor, VariableControlFlowState>>() {
313 @Override
314 public void execute(
315 @NotNull Instruction instruction,
316 @Nullable Map<VariableDescriptor, VariableControlFlowState> in,
317 @Nullable Map<VariableDescriptor, VariableControlFlowState> out
318 ) {
319 assert in != null && out != null;
320 VariableInitContext ctxt =
321 new VariableInitContext(instruction, reportedDiagnosticMap, in, out, lexicalScopeVariableInfo);
322 if (ctxt.variableDescriptor == null) return;
323 if (instruction instanceof ReadValueInstruction) {
324 ReadValueInstruction readValueInstruction = (ReadValueInstruction) instruction;
325 KtElement element = readValueInstruction.getElement();
326 if (PseudocodeUtil.isThisOrNoDispatchReceiver(readValueInstruction, trace.getBindingContext()) &&
327 declaredVariables.contains(ctxt.variableDescriptor)) {
328 checkIsInitialized(ctxt, element, varWithUninitializedErrorGenerated);
329 }
330 return;
331 }
332 if (!(instruction instanceof WriteValueInstruction)) return;
333 WriteValueInstruction writeValueInstruction = (WriteValueInstruction) instruction;
334 KtElement element = writeValueInstruction.getLValue();
335 if (!(element instanceof KtExpression)) return;
336 boolean error = checkValReassignment(ctxt, (KtExpression) element, writeValueInstruction,
337 varWithValReassignErrorGenerated);
338 if (!error && processClassOrObject) {
339 error = checkAssignmentBeforeDeclaration(ctxt, (KtExpression) element);
340 }
341 if (!error && processClassOrObject) {
342 checkInitializationForCustomSetter(ctxt, (KtExpression) element);
343 }
344 }
345 }
346 );
347 }
348
349 public void recordInitializedVariables() {
350 PseudocodeVariablesData pseudocodeVariablesData = getPseudocodeVariablesData();
351 Pseudocode pseudocode = pseudocodeVariablesData.getPseudocode();
352 Map<Instruction, Edges<InitControlFlowInfo>> initializers = pseudocodeVariablesData.getVariableInitializers();
353 recordInitializedVariables(pseudocode, initializers);
354 for (LocalFunctionDeclarationInstruction instruction : pseudocode.getLocalDeclarations()) {
355 recordInitializedVariables(instruction.getBody(), initializers);
356 }
357 }
358
359 private boolean isDefinitelyInitialized(@NotNull PropertyDescriptor propertyDescriptor) {
360 if (propertyDescriptor.isLateInit()) return true;
361 if (trace.get(BACKING_FIELD_REQUIRED, propertyDescriptor) == Boolean.TRUE) return false;
362 PsiElement property = DescriptorToSourceUtils.descriptorToDeclaration(propertyDescriptor);
363 if (property instanceof KtProperty && ((KtProperty) property).hasDelegate()) return false;
364 return true;
365 }
366
367 private void checkIsInitialized(
368 @NotNull VariableInitContext ctxt,
369 @NotNull KtElement element,
370 @NotNull Collection<VariableDescriptor> varWithUninitializedErrorGenerated
371 ) {
372 if (!(element instanceof KtSimpleNameExpression)) return;
373
374
375 boolean isDefinitelyInitialized = ctxt.exitInitState.definitelyInitialized();
376 VariableDescriptor variableDescriptor = ctxt.variableDescriptor;
377 if (!isDefinitelyInitialized && variableDescriptor instanceof PropertyDescriptor) {
378 isDefinitelyInitialized = isDefinitelyInitialized((PropertyDescriptor) variableDescriptor);
379 }
380 if (!isDefinitelyInitialized && !varWithUninitializedErrorGenerated.contains(variableDescriptor)) {
381 if (!(variableDescriptor instanceof PropertyDescriptor)) {
382 varWithUninitializedErrorGenerated.add(variableDescriptor);
383 }
384 if (variableDescriptor instanceof ValueParameterDescriptor) {
385 report(Errors.UNINITIALIZED_PARAMETER.on((KtSimpleNameExpression) element,
386 (ValueParameterDescriptor) variableDescriptor), ctxt);
387 }
388 else {
389 report(Errors.UNINITIALIZED_VARIABLE.on((KtSimpleNameExpression) element, variableDescriptor), ctxt);
390 }
391 }
392 }
393
394 private boolean checkValReassignment(
395 @NotNull VariableInitContext ctxt,
396 @NotNull KtExpression expression,
397 @NotNull WriteValueInstruction writeValueInstruction,
398 @NotNull Collection<VariableDescriptor> varWithValReassignErrorGenerated
399 ) {
400 VariableDescriptor variableDescriptor = ctxt.variableDescriptor;
401 PropertyDescriptor propertyDescriptor = SyntheticFieldDescriptorKt.getReferencedProperty(variableDescriptor);
402 if (KtPsiUtil.isBackingFieldReference(variableDescriptor) && propertyDescriptor != null) {
403 KtPropertyAccessor accessor = PsiTreeUtil.getParentOfType(expression, KtPropertyAccessor.class);
404 if (accessor != null) {
405 DeclarationDescriptor accessorDescriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, accessor);
406 if (propertyDescriptor.getGetter() == accessorDescriptor) {
407 //val can be reassigned through backing field inside its own getter
408 return false;
409 }
410 }
411 }
412
413 boolean mayBeInitializedNotHere = ctxt.enterInitState.mayBeInitialized();
414 boolean hasBackingField = true;
415 if (variableDescriptor instanceof PropertyDescriptor) {
416 hasBackingField = trace.get(BindingContext.BACKING_FIELD_REQUIRED, (PropertyDescriptor) variableDescriptor);
417 }
418 if (variableDescriptor.isVar() && variableDescriptor instanceof PropertyDescriptor) {
419 DeclarationDescriptor descriptor = BindingContextUtils.getEnclosingDescriptor(trace.getBindingContext(), expression);
420 PropertySetterDescriptor setterDescriptor = ((PropertyDescriptor) variableDescriptor).getSetter();
421
422 ResolvedCall<? extends CallableDescriptor> resolvedCall = CallUtilKt.getResolvedCall(expression, trace.getBindingContext());
423 ReceiverValue receiverValue = null;
424 if (resolvedCall != null) {
425 receiverValue = resolvedCall.getDispatchReceiver();
426 }
427
428 if (Visibilities.isVisible(receiverValue, variableDescriptor, descriptor) && setterDescriptor != null
429 && !Visibilities.isVisible(receiverValue, setterDescriptor, descriptor)) {
430 report(Errors.INVISIBLE_SETTER.on(expression, variableDescriptor, setterDescriptor.getVisibility(),
431 setterDescriptor), ctxt);
432 return true;
433 }
434 }
435 boolean isThisOrNoDispatchReceiver =
436 PseudocodeUtil.isThisOrNoDispatchReceiver(writeValueInstruction, trace.getBindingContext());
437 if ((mayBeInitializedNotHere || !hasBackingField || !isThisOrNoDispatchReceiver) && !variableDescriptor.isVar()) {
438 boolean hasReassignMethodReturningUnit = false;
439 KtSimpleNameExpression operationReference = null;
440 PsiElement parent = expression.getParent();
441 if (parent instanceof KtBinaryExpression) {
442 operationReference = ((KtBinaryExpression) parent).getOperationReference();
443 }
444 else if (parent instanceof KtUnaryExpression) {
445 operationReference = ((KtUnaryExpression) parent).getOperationReference();
446 }
447 if (operationReference != null) {
448 DeclarationDescriptor descriptor = trace.get(BindingContext.REFERENCE_TARGET, operationReference);
449 if (descriptor instanceof FunctionDescriptor) {
450 if (KotlinBuiltIns.isUnit(((FunctionDescriptor) descriptor).getReturnType())) {
451 hasReassignMethodReturningUnit = true;
452 }
453 }
454 if (descriptor == null) {
455 Collection<? extends DeclarationDescriptor> descriptors =
456 trace.get(BindingContext.AMBIGUOUS_REFERENCE_TARGET, operationReference);
457 if (descriptors != null) {
458 for (DeclarationDescriptor referenceDescriptor : descriptors) {
459 if (KotlinBuiltIns.isUnit(((FunctionDescriptor) referenceDescriptor).getReturnType())) {
460 hasReassignMethodReturningUnit = true;
461 }
462 }
463 }
464 }
465 }
466 if (!hasReassignMethodReturningUnit) {
467 if (!isThisOrNoDispatchReceiver || !varWithValReassignErrorGenerated.contains(variableDescriptor)) {
468 report(Errors.VAL_REASSIGNMENT.on(expression, variableDescriptor), ctxt);
469 }
470 if (isThisOrNoDispatchReceiver) {
471 // try to get rid of repeating VAL_REASSIGNMENT diagnostic only for vars with no receiver
472 // or when receiver is this
473 varWithValReassignErrorGenerated.add(variableDescriptor);
474 }
475 return true;
476 }
477 }
478 return false;
479 }
480
481 private boolean checkAssignmentBeforeDeclaration(@NotNull VariableInitContext ctxt, @NotNull KtExpression expression) {
482 if (!ctxt.enterInitState.isDeclared() && !ctxt.exitInitState.isDeclared()
483 && !ctxt.enterInitState.mayBeInitialized() && ctxt.exitInitState.mayBeInitialized()) {
484 report(Errors.INITIALIZATION_BEFORE_DECLARATION.on(expression, ctxt.variableDescriptor), ctxt);
485 return true;
486 }
487 return false;
488 }
489
490 private boolean checkInitializationForCustomSetter(@NotNull VariableInitContext ctxt, @NotNull KtExpression expression) {
491 VariableDescriptor variableDescriptor = ctxt.variableDescriptor;
492 if (!(variableDescriptor instanceof PropertyDescriptor)
493 || ctxt.enterInitState.mayBeInitialized()
494 || !ctxt.exitInitState.mayBeInitialized()
495 || !variableDescriptor.isVar()
496 || !trace.get(BindingContext.BACKING_FIELD_REQUIRED, (PropertyDescriptor) variableDescriptor)
497 ) {
498 return false;
499 }
500
501 PsiElement property = DescriptorToSourceUtils.descriptorToDeclaration(variableDescriptor);
502 assert property instanceof KtProperty;
503 KtPropertyAccessor setter = ((KtProperty) property).getSetter();
504 if (((PropertyDescriptor) variableDescriptor).getModality() == Modality.FINAL && (setter == null || !setter.hasBody())) {
505 return false;
506 }
507
508 KtExpression variable = expression;
509 if (expression instanceof KtDotQualifiedExpression) {
510 if (((KtDotQualifiedExpression) expression).getReceiverExpression() instanceof KtThisExpression) {
511 variable = ((KtDotQualifiedExpression) expression).getSelectorExpression();
512 }
513 }
514 if (variable instanceof KtSimpleNameExpression) {
515 trace.record(IS_UNINITIALIZED, (PropertyDescriptor) variableDescriptor);
516 return true;
517 }
518 return false;
519 }
520
521 private void recordInitializedVariables(
522 @NotNull Pseudocode pseudocode,
523 @NotNull Map<Instruction, Edges<InitControlFlowInfo>> initializersMap
524 ) {
525 Edges<InitControlFlowInfo> initializers = initializersMap.get(pseudocode.getExitInstruction());
526 if (initializers == null) return;
527 Set<VariableDescriptor> declaredVariables = getPseudocodeVariablesData().getDeclaredVariables(pseudocode, false);
528 for (VariableDescriptor variable : declaredVariables) {
529 if (variable instanceof PropertyDescriptor) {
530 VariableControlFlowState variableControlFlowState = initializers.getIncoming().get(variable);
531 if (variableControlFlowState != null && variableControlFlowState.definitelyInitialized()) continue;
532 trace.record(BindingContext.IS_UNINITIALIZED, (PropertyDescriptor) variable);
533 }
534 }
535 }
536
537 ////////////////////////////////////////////////////////////////////////////////
538 // "Unused variable" & "unused value" analyses
539
540 public void markUnusedVariables() {
541 final PseudocodeVariablesData pseudocodeVariablesData = getPseudocodeVariablesData();
542 Map<Instruction, Edges<UseControlFlowInfo>> variableStatusData = 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 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 List<Instruction> usages = pseudocode.getUsages(value);
689 boolean isUsedAsExpression = !usages.isEmpty();
690 boolean isUsedAsResultOfLambda = isUsedAsResultOfLambda(usages);
691 for (KtElement element : pseudocode.getValueElements(value)) {
692 trace.record(BindingContext.USED_AS_EXPRESSION, element, isUsedAsExpression);
693 trace.record(BindingContext.USED_AS_RESULT_OF_LAMBDA, element, isUsedAsResultOfLambda);
694 }
695 }
696 }
697 );
698 }
699
700 private static boolean isUsedAsResultOfLambda(List<Instruction> usages) {
701 for (Instruction usage : usages) {
702 if (usage instanceof ReturnValueInstruction) {
703 KtElement returnElement = ((ReturnValueInstruction) usage).getElement();
704 PsiElement parentElement = returnElement.getParent();
705 if (!(returnElement instanceof KtReturnExpression ||
706 parentElement instanceof KtDeclaration && !(parentElement instanceof KtFunctionLiteral))) {
707 return true;
708 }
709 }
710 }
711 return false;
712 }
713
714 public void checkIfExpressions() {
715 PseudocodeTraverserKt.traverse(
716 pseudocode, TraversalOrder.FORWARD, new ControlFlowInformationProvider.FunctionVoid1<Instruction>() {
717 @Override
718 public void execute(@NotNull Instruction instruction) {
719 PseudoValue value = instruction instanceof InstructionWithValue
720 ? ((InstructionWithValue) instruction).getOutputValue()
721 : null;
722 for (KtElement element : instruction.getOwner().getValueElements(value)) {
723 if (!(element instanceof KtIfExpression)) continue;
724 KtIfExpression ifExpression = (KtIfExpression) element;
725
726 if (BindingContextUtilsKt.isUsedAsExpression(ifExpression, trace.getBindingContext())) {
727 KtExpression thenExpression = ifExpression.getThen();
728 KtExpression elseExpression = ifExpression.getElse();
729
730 if (thenExpression == null || elseExpression == null) {
731 trace.report(INVALID_IF_AS_EXPRESSION.on(ifExpression));
732 }
733 else {
734 checkImplicitCastOnConditionalExpression(ifExpression);
735 }
736 }
737 }
738 }
739 }
740 );
741 }
742
743 private static List<KtExpression> collectResultingExpressionsOfConditionalExpression(KtExpression expression) {
744 List<KtExpression> leafBranches = new ArrayList<KtExpression>();
745 collectResultingExpressionsOfConditionalExpressionRec(expression, leafBranches);
746 return leafBranches;
747 }
748
749 private static void collectResultingExpressionsOfConditionalExpressionRec(
750 @Nullable KtExpression expression,
751 @NotNull List<KtExpression> resultingExpressions
752 ) {
753 if (expression instanceof KtIfExpression) {
754 KtIfExpression ifExpression = (KtIfExpression) expression;
755 collectResultingExpressionsOfConditionalExpressionRec(ifExpression.getThen(), resultingExpressions);
756 collectResultingExpressionsOfConditionalExpressionRec(ifExpression.getElse(), resultingExpressions);
757 }
758 else if (expression instanceof KtWhenExpression) {
759 KtWhenExpression whenExpression = (KtWhenExpression) expression;
760 for (KtWhenEntry whenEntry : whenExpression.getEntries()) {
761 collectResultingExpressionsOfConditionalExpressionRec(whenEntry.getExpression(), resultingExpressions);
762 }
763 }
764 else if (expression != null){
765 KtExpression resultingExpression = getResultingExpression(expression);
766 if (resultingExpression instanceof KtIfExpression || resultingExpression instanceof KtWhenExpression) {
767 collectResultingExpressionsOfConditionalExpressionRec(resultingExpression, resultingExpressions);
768 }
769 else {
770 resultingExpressions.add(resultingExpression);
771 }
772 }
773 }
774
775 private void checkImplicitCastOnConditionalExpression(@NotNull KtExpression expression) {
776 Collection<KtExpression> branchExpressions = collectResultingExpressionsOfConditionalExpression(expression);
777
778 KotlinType expectedExpressionType = trace.get(EXPECTED_EXPRESSION_TYPE, expression);
779 if (expectedExpressionType != null && expectedExpressionType != DONT_CARE) return;
780
781 KotlinType expressionType = trace.getType(expression);
782 if (expressionType == null) {
783 return;
784 }
785 if (KotlinBuiltIns.isAnyOrNullableAny(expressionType)) {
786 boolean isUsedAsResultOfLambda = BindingContextUtilsKt.isUsedAsResultOfLambda(expression, trace.getBindingContext());
787 for (KtExpression branchExpression : branchExpressions) {
788 if (branchExpression == null) continue;
789 KotlinType branchType = trace.getType(branchExpression);
790 if (branchType == null
791 || KotlinBuiltIns.isAnyOrNullableAny(branchType)
792 || (isUsedAsResultOfLambda && KotlinBuiltIns.isUnitOrNullableUnit(branchType))) {
793 return;
794 }
795 }
796 for (KtExpression branchExpression : branchExpressions) {
797 if (branchExpression == null) continue;
798 KotlinType branchType = trace.getType(branchExpression);
799 if (branchType == null) continue;
800 if (KotlinBuiltIns.isNothing(branchType)) continue;
801 trace.report(IMPLICIT_CAST_TO_ANY.on(getResultingExpression(branchExpression), branchType, expressionType));
802 }
803 }
804 }
805
806 private static @NotNull KtExpression getResultingExpression(@NotNull KtExpression expression) {
807 KtExpression finger = expression;
808 while (true) {
809 KtExpression deparenthesized = KtPsiUtil.deparenthesize(finger);
810 deparenthesized = KtPsiUtil.getExpressionOrLastStatementInBlock(deparenthesized);
811 if (deparenthesized == null || deparenthesized == finger) break;
812 finger = deparenthesized;
813 }
814 return finger;
815 }
816
817 public void checkWhenExpressions() {
818 final Map<Instruction, Edges<InitControlFlowInfo>> initializers = pseudocodeVariablesData.getVariableInitializers();
819 PseudocodeTraverserKt.traverse(
820 pseudocode, TraversalOrder.FORWARD, new ControlFlowInformationProvider.FunctionVoid1<Instruction>() {
821 @Override
822 public void execute(@NotNull Instruction instruction) {
823 if (instruction instanceof MagicInstruction) {
824 MagicInstruction magicInstruction = (MagicInstruction) instruction;
825 if (magicInstruction.getKind() == MagicKind.EXHAUSTIVE_WHEN_ELSE) {
826 Instruction next = magicInstruction.getNext();
827 if (next instanceof MergeInstruction) {
828 MergeInstruction mergeInstruction = (MergeInstruction) next;
829 if (initializers.containsKey(mergeInstruction) && initializers.containsKey(magicInstruction)) {
830 InitControlFlowInfo mergeInfo = initializers.get(mergeInstruction).getIncoming();
831 InitControlFlowInfo magicInfo = initializers.get(magicInstruction).getOutgoing();
832 if (mergeInstruction.getElement() instanceof KtWhenExpression &&
833 magicInfo.checkDefiniteInitializationInWhen(mergeInfo)) {
834 trace.record(IMPLICIT_EXHAUSTIVE_WHEN, (KtWhenExpression) mergeInstruction.getElement());
835 }
836 }
837 }
838 }
839 }
840 PseudoValue value = instruction instanceof InstructionWithValue
841 ? ((InstructionWithValue) instruction).getOutputValue()
842 : null;
843 for (KtElement element : instruction.getOwner().getValueElements(value)) {
844 if (!(element instanceof KtWhenExpression)) continue;
845 KtWhenExpression whenExpression = (KtWhenExpression) element;
846
847 if (BindingContextUtilsKt.isUsedAsExpression(whenExpression, trace.getBindingContext())) {
848 checkImplicitCastOnConditionalExpression(whenExpression);
849 }
850
851 if (whenExpression.getElseExpression() != null) continue;
852
853 BindingContext context = trace.getBindingContext();
854 List<WhenMissingCase> necessaryCases = WhenChecker.getNecessaryCases(whenExpression, context);
855 if (!necessaryCases.isEmpty()) {
856 trace.report(NO_ELSE_IN_WHEN.on(whenExpression, necessaryCases));
857 }
858 else if (whenExpression.getSubjectExpression() != null) {
859 ClassDescriptor enumClassDescriptor = WhenChecker.getClassDescriptorOfTypeIfEnum(
860 trace.getType(whenExpression.getSubjectExpression()));
861 if (enumClassDescriptor != null) {
862 List<WhenMissingCase> missingCases = WhenChecker.getEnumMissingCases(
863 whenExpression, context, enumClassDescriptor
864 );
865 if (!missingCases.isEmpty()) {
866 trace.report(NON_EXHAUSTIVE_WHEN.on(whenExpression, missingCases));
867 }
868 }
869 }
870 }
871 }
872 }
873 );
874 }
875
876 ////////////////////////////////////////////////////////////////////////////////
877 // Tail calls
878
879 public void markTailCalls() {
880 final DeclarationDescriptor subroutineDescriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, subroutine);
881 if (!(subroutineDescriptor instanceof FunctionDescriptor)) return;
882 if (!((FunctionDescriptor) subroutineDescriptor).isTailrec()) return;
883
884 // finally blocks are copied which leads to multiple diagnostics reported on one instruction
885 class KindAndCall {
886 TailRecursionKind kind;
887 ResolvedCall<?> call;
888
889 KindAndCall(TailRecursionKind kind, ResolvedCall<?> call) {
890 this.kind = kind;
891 this.call = call;
892 }
893 }
894 final Map<KtElement, KindAndCall> calls = new HashMap<KtElement, KindAndCall>();
895 PseudocodeTraverserKt.traverse(
896 pseudocode,
897 TraversalOrder.FORWARD,
898 new FunctionVoid1<Instruction>() {
899 public void execute(@NotNull Instruction instruction) {
900 if (!(instruction instanceof CallInstruction)) return;
901 CallInstruction callInstruction = (CallInstruction) instruction;
902
903 ResolvedCall<?> resolvedCall = CallUtilKt.getResolvedCall(callInstruction.getElement(), trace.getBindingContext());
904 if (resolvedCall == null) return;
905
906 // is this a recursive call?
907 CallableDescriptor functionDescriptor = resolvedCall.getResultingDescriptor();
908 if (!functionDescriptor.getOriginal().equals(subroutineDescriptor)) return;
909
910 KtElement element = callInstruction.getElement();
911 //noinspection unchecked
912 KtExpression parent = PsiTreeUtil.getParentOfType(
913 element,
914 KtTryExpression.class, KtFunction.class, KtAnonymousInitializer.class
915 );
916
917 if (parent instanceof KtTryExpression) {
918 // We do not support tail calls Collections.singletonMap() try-catch-finally, for simplicity of the mental model
919 // very few cases there would be real tail-calls, and it's often not so easy for the user to see why
920 calls.put(element, new KindAndCall(IN_TRY, resolvedCall));
921 return;
922 }
923
924 boolean isTail = PseudocodeTraverserKt.traverseFollowingInstructions(
925 callInstruction,
926 new HashSet<Instruction>(),
927 TraversalOrder.FORWARD,
928 new TailRecursionDetector(subroutine, callInstruction)
929 );
930
931 // A tail call is not allowed to change dispatch receiver
932 // class C {
933 // fun foo(other: C) {
934 // other.foo(this) // not a tail call
935 // }
936 // }
937 boolean sameDispatchReceiver =
938 ResolvedCallUtilKt.hasThisOrNoDispatchReceiver(resolvedCall, trace.getBindingContext());
939
940 TailRecursionKind kind = isTail && sameDispatchReceiver ? TAIL_CALL : NON_TAIL;
941
942 KindAndCall kindAndCall = calls.get(element);
943 calls.put(element,
944 new KindAndCall(
945 combineKinds(kind, kindAndCall == null ? null : kindAndCall.kind),
946 resolvedCall
947 )
948 );
949 }
950 }
951 );
952 boolean hasTailCalls = false;
953 for (Map.Entry<KtElement, KindAndCall> entry : calls.entrySet()) {
954 KtElement element = entry.getKey();
955 KindAndCall kindAndCall = entry.getValue();
956 switch (kindAndCall.kind) {
957 case TAIL_CALL:
958 trace.record(TAIL_RECURSION_CALL, kindAndCall.call, TailRecursionKind.TAIL_CALL);
959 hasTailCalls = true;
960 break;
961 case IN_TRY:
962 trace.report(Errors.TAIL_RECURSION_IN_TRY_IS_NOT_SUPPORTED.on(element));
963 break;
964 case NON_TAIL:
965 trace.report(Errors.NON_TAIL_RECURSIVE_CALL.on(element));
966 break;
967 }
968 }
969
970 if (!hasTailCalls) {
971 trace.report(Errors.NO_TAIL_CALLS_FOUND.on((KtNamedFunction) subroutine));
972 }
973 }
974
975 private static TailRecursionKind combineKinds(TailRecursionKind kind, @Nullable TailRecursionKind existingKind) {
976 TailRecursionKind resultingKind;
977 if (existingKind == null || existingKind == kind) {
978 resultingKind = kind;
979 }
980 else {
981 if (check(kind, existingKind, IN_TRY, TAIL_CALL)) {
982 resultingKind = IN_TRY;
983 }
984 else if (check(kind, existingKind, IN_TRY, NON_TAIL)) {
985 resultingKind = IN_TRY;
986 }
987 else {
988 // TAIL_CALL, NON_TAIL
989 resultingKind = NON_TAIL;
990 }
991 }
992 return resultingKind;
993 }
994
995 private static boolean check(Object a, Object b, Object x, Object y) {
996 return (a == x && b == y) || (a == y && b == x);
997 }
998
999 ////////////////////////////////////////////////////////////////////////////////
1000 // Utility classes and methods
1001
1002 /**
1003 * The method provides reporting of the same diagnostic only once for copied instructions
1004 * (depends on whether it should be reported for all or only for one of the copies)
1005 */
1006 private void report(
1007 @NotNull Diagnostic diagnostic,
1008 @NotNull VariableContext ctxt
1009 ) {
1010 Instruction instruction = ctxt.instruction;
1011 if (instruction.getCopies().isEmpty()) {
1012 trace.report(diagnostic);
1013 return;
1014 }
1015 Map<Instruction, DiagnosticFactory<?>> previouslyReported = ctxt.reportedDiagnosticMap;
1016 previouslyReported.put(instruction, diagnostic.getFactory());
1017
1018 boolean alreadyReported = false;
1019 boolean sameErrorForAllCopies = true;
1020 for (Instruction copy : instruction.getCopies()) {
1021 DiagnosticFactory<?> previouslyReportedErrorFactory = previouslyReported.get(copy);
1022 if (previouslyReportedErrorFactory != null) {
1023 alreadyReported = true;
1024 }
1025
1026 if (previouslyReportedErrorFactory != diagnostic.getFactory()) {
1027 sameErrorForAllCopies = false;
1028 }
1029 }
1030
1031 if (mustBeReportedOnAllCopies(diagnostic.getFactory())) {
1032 if (sameErrorForAllCopies) {
1033 trace.report(diagnostic);
1034 }
1035 }
1036 else {
1037 //only one reporting required
1038 if (!alreadyReported) {
1039 trace.report(diagnostic);
1040 }
1041 }
1042 }
1043
1044 private static boolean mustBeReportedOnAllCopies(@NotNull DiagnosticFactory<?> diagnosticFactory) {
1045 return diagnosticFactory == UNUSED_VARIABLE
1046 || diagnosticFactory == UNUSED_PARAMETER
1047 || diagnosticFactory == UNUSED_CHANGED_VALUE;
1048 }
1049
1050
1051 private class VariableContext {
1052 final Map<Instruction, DiagnosticFactory<?>> reportedDiagnosticMap;
1053 final Instruction instruction;
1054 final VariableDescriptor variableDescriptor;
1055
1056 private VariableContext(
1057 @NotNull Instruction instruction,
1058 @NotNull Map<Instruction, DiagnosticFactory<?>> map
1059 ) {
1060 this.instruction = instruction;
1061 reportedDiagnosticMap = map;
1062 variableDescriptor = PseudocodeUtil.extractVariableDescriptorIfAny(instruction, true, trace.getBindingContext());
1063 }
1064 }
1065
1066 private class VariableInitContext extends VariableContext {
1067 final VariableControlFlowState enterInitState;
1068 final VariableControlFlowState exitInitState;
1069
1070 private VariableInitContext(
1071 @NotNull Instruction instruction,
1072 @NotNull Map<Instruction, DiagnosticFactory<?>> map,
1073 @NotNull Map<VariableDescriptor, VariableControlFlowState> in,
1074 @NotNull Map<VariableDescriptor, VariableControlFlowState> out,
1075 @NotNull LexicalScopeVariableInfo lexicalScopeVariableInfo
1076 ) {
1077 super(instruction, map);
1078 enterInitState = initialize(variableDescriptor, lexicalScopeVariableInfo, in);
1079 exitInitState = initialize(variableDescriptor, lexicalScopeVariableInfo, out);
1080 }
1081
1082 private VariableControlFlowState initialize(
1083 VariableDescriptor variableDescriptor,
1084 LexicalScopeVariableInfo lexicalScopeVariableInfo,
1085 Map<VariableDescriptor, VariableControlFlowState> map
1086 ) {
1087 if (variableDescriptor == null) return null;
1088 VariableControlFlowState state = map.get(variableDescriptor);
1089 if (state != null) return state;
1090 return PseudocodeVariablesData.getDefaultValueForInitializers(variableDescriptor, instruction, lexicalScopeVariableInfo);
1091 }
1092 }
1093
1094 private class VariableUseContext extends VariableContext {
1095 final VariableUseState enterUseState;
1096 final VariableUseState exitUseState;
1097
1098
1099 private VariableUseContext(
1100 @NotNull Instruction instruction,
1101 @NotNull Map<Instruction, DiagnosticFactory<?>> map,
1102 @NotNull Map<VariableDescriptor, VariableUseState> in,
1103 @NotNull Map<VariableDescriptor, VariableUseState> out
1104 ) {
1105 super(instruction, map);
1106 enterUseState = variableDescriptor != null ? in.get(variableDescriptor) : null;
1107 exitUseState = variableDescriptor != null ? out.get(variableDescriptor) : null;
1108 }
1109 }
1110
1111 //TODO after KT-4621 rewrite to Kotlin
1112 public abstract static class InstructionDataAnalyzeStrategy<D> implements Function3<Instruction, D, D, Unit> {
1113 @Override
1114 public Unit invoke(Instruction instruction, D enterData, D exitData) {
1115 execute(instruction, enterData, exitData);
1116 return Unit.INSTANCE;
1117 }
1118
1119 public abstract void execute(Instruction instruction, D enterData, D exitData);
1120 }
1121
1122 public abstract static class FunctionVoid1<P> implements Function1<P, Unit> {
1123 @Override
1124 public Unit invoke(P p) {
1125 execute(p);
1126 return Unit.INSTANCE;
1127 }
1128
1129 public abstract void execute(P p);
1130 }
1131 }