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