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