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