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