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