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