001    /*
002     * Copyright 2010-2013 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.jet.lang.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.jet.lang.cfg.PseudocodeVariablesData.VariableInitState;
031    import org.jetbrains.jet.lang.cfg.PseudocodeVariablesData.VariableUseState;
032    import org.jetbrains.jet.lang.cfg.pseudocode.PseudoValue;
033    import org.jetbrains.jet.lang.cfg.pseudocode.Pseudocode;
034    import org.jetbrains.jet.lang.cfg.pseudocode.PseudocodePackage;
035    import org.jetbrains.jet.lang.cfg.pseudocode.PseudocodeUtil;
036    import org.jetbrains.jet.lang.cfg.pseudocode.instructions.Instruction;
037    import org.jetbrains.jet.lang.cfg.pseudocode.instructions.InstructionVisitor;
038    import org.jetbrains.jet.lang.cfg.pseudocode.instructions.JetElementInstruction;
039    import org.jetbrains.jet.lang.cfg.pseudocode.instructions.eval.*;
040    import org.jetbrains.jet.lang.cfg.pseudocode.instructions.jumps.*;
041    import org.jetbrains.jet.lang.cfg.pseudocode.instructions.special.LocalFunctionDeclarationInstruction;
042    import org.jetbrains.jet.lang.cfg.pseudocode.instructions.special.MarkInstruction;
043    import org.jetbrains.jet.lang.cfg.pseudocode.instructions.special.SubroutineExitInstruction;
044    import org.jetbrains.jet.lang.cfg.pseudocode.instructions.special.VariableDeclarationInstruction;
045    import org.jetbrains.jet.lang.cfg.pseudocodeTraverser.Edges;
046    import org.jetbrains.jet.lang.cfg.pseudocodeTraverser.PseudocodeTraverserPackage;
047    import org.jetbrains.jet.lang.cfg.pseudocodeTraverser.TraversalOrder;
048    import org.jetbrains.jet.lang.descriptors.*;
049    import org.jetbrains.jet.lang.diagnostics.Diagnostic;
050    import org.jetbrains.jet.lang.diagnostics.DiagnosticFactory;
051    import org.jetbrains.jet.lang.diagnostics.Errors;
052    import org.jetbrains.jet.lang.psi.*;
053    import org.jetbrains.jet.lang.resolve.*;
054    import org.jetbrains.jet.lang.resolve.bindingContextUtil.BindingContextUtilPackage;
055    import org.jetbrains.jet.lang.resolve.calls.TailRecursionKind;
056    import org.jetbrains.jet.lang.resolve.calls.callUtil.CallUtilPackage;
057    import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
058    import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver;
059    import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue;
060    import org.jetbrains.jet.lang.resolve.scopes.receivers.ThisReceiver;
061    import org.jetbrains.jet.lang.types.JetType;
062    import org.jetbrains.jet.lang.types.expressions.ExpressionTypingUtils;
063    import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
064    import org.jetbrains.jet.lexer.JetTokens;
065    import org.jetbrains.jet.plugin.MainFunctionDetector;
066    
067    import java.util.*;
068    
069    import static org.jetbrains.jet.lang.cfg.PseudocodeVariablesData.VariableUseState.*;
070    import static org.jetbrains.jet.lang.cfg.pseudocodeTraverser.TraversalOrder.FORWARD;
071    import static org.jetbrains.jet.lang.diagnostics.Errors.*;
072    import static org.jetbrains.jet.lang.resolve.BindingContext.CAPTURED_IN_CLOSURE;
073    import static org.jetbrains.jet.lang.resolve.BindingContext.TAIL_RECURSION_CALL;
074    import static org.jetbrains.jet.lang.resolve.calls.TailRecursionKind.*;
075    import static org.jetbrains.jet.lang.resolve.calls.callUtil.CallUtilPackage.getResolvedCall;
076    import static org.jetbrains.jet.lang.types.TypeUtils.NO_EXPECTED_TYPE;
077    import static org.jetbrains.jet.lang.types.TypeUtils.noExpectedType;
078    
079    public class JetFlowInformationProvider {
080    
081        private final JetElement subroutine;
082        private final Pseudocode pseudocode;
083        private final BindingTrace trace;
084        private PseudocodeVariablesData pseudocodeVariablesData;
085    
086        private JetFlowInformationProvider(
087                @NotNull JetElement declaration,
088                @NotNull BindingTrace trace,
089                @NotNull Pseudocode pseudocode
090        ) {
091            this.subroutine = declaration;
092            this.trace = trace;
093            this.pseudocode = pseudocode;
094        }
095    
096        public JetFlowInformationProvider(
097                @NotNull JetElement declaration,
098                @NotNull BindingTrace trace
099        ) {
100            this(declaration, trace, new JetControlFlowProcessor(trace).generatePseudocode(declaration));
101        }
102    
103        public PseudocodeVariablesData getPseudocodeVariablesData() {
104            if (pseudocodeVariablesData == null) {
105                pseudocodeVariablesData = new PseudocodeVariablesData(pseudocode, trace.getBindingContext());
106            }
107            return pseudocodeVariablesData;
108        }
109    
110        public void checkForLocalClassOrObjectMode() {
111            // Local classes and objects are analyzed twice: when TopDownAnalyzer processes it and as a part of its container.
112            // Almost all checks can be done when the container is analyzed
113            // except recording initialized variables (this information is needed for DeclarationChecker).
114            recordInitializedVariables();
115        }
116    
117        public void checkDeclaration() {
118    
119            recordInitializedVariables();
120    
121            checkLocalFunctions();
122    
123            markUninitializedVariables();
124    
125            markUnusedVariables();
126    
127            markStatements();
128    
129            markUnusedExpressions();
130    
131            markWhenWithoutElse();
132        }
133    
134        public void checkFunction(@Nullable JetType expectedReturnType) {
135            UnreachableCode unreachableCode = collectUnreachableCode();
136            reportUnreachableCode(unreachableCode);
137    
138            if (subroutine instanceof JetFunctionLiteral) return;
139    
140            checkDefiniteReturn(expectedReturnType != null ? expectedReturnType : NO_EXPECTED_TYPE, unreachableCode);
141    
142            markTailCalls();
143        }
144    
145        private void collectReturnExpressions(@NotNull final Collection<JetElement> returnedExpressions) {
146            final Set<Instruction> instructions = Sets.newHashSet(pseudocode.getInstructions());
147            SubroutineExitInstruction exitInstruction = pseudocode.getExitInstruction();
148            for (Instruction previousInstruction : exitInstruction.getPreviousInstructions()) {
149                previousInstruction.accept(new InstructionVisitor() {
150                    @Override
151                    public void visitReturnValue(ReturnValueInstruction instruction) {
152                        if (instructions.contains(instruction)) { //exclude non-local return expressions
153                            returnedExpressions.add(instruction.getElement());
154                        }
155                    }
156    
157                    @Override
158                    public void visitReturnNoValue(ReturnNoValueInstruction instruction) {
159                        if (instructions.contains(instruction)) {
160                            returnedExpressions.add(instruction.getElement());
161                        }
162                    }
163    
164    
165                    @Override
166                    public void visitJump(AbstractJumpInstruction instruction) {
167                        // Nothing
168                    }
169    
170                    @Override
171                    public void visitUnconditionalJump(UnconditionalJumpInstruction instruction) {
172                        redirectToPrevInstructions(instruction);
173                    }
174    
175                    private void redirectToPrevInstructions(Instruction instruction) {
176                        for (Instruction previousInstruction : instruction.getPreviousInstructions()) {
177                            previousInstruction.accept(this);
178                        }
179                    }
180    
181                    @Override
182                    public void visitNondeterministicJump(NondeterministicJumpInstruction instruction) {
183                        redirectToPrevInstructions(instruction);
184                    }
185    
186                    @Override
187                    public void visitMarkInstruction(MarkInstruction instruction) {
188                        redirectToPrevInstructions(instruction);
189                    }
190    
191                    @Override
192                    public void visitInstruction(Instruction instruction) {
193                        if (instruction instanceof JetElementInstruction) {
194                            JetElementInstruction elementInstruction = (JetElementInstruction) instruction;
195                            returnedExpressions.add(elementInstruction.getElement());
196                        }
197                        else {
198                            throw new IllegalStateException(instruction + " precedes the exit point");
199                        }
200                    }
201                });
202            }
203        }
204    
205        private void checkLocalFunctions() {
206            for (LocalFunctionDeclarationInstruction localDeclarationInstruction : pseudocode.getLocalDeclarations()) {
207                JetElement element = localDeclarationInstruction.getElement();
208                if (element instanceof JetDeclarationWithBody) {
209                    JetDeclarationWithBody localDeclaration = (JetDeclarationWithBody) element;
210    
211                    CallableDescriptor functionDescriptor =
212                            (CallableDescriptor) trace.getBindingContext().get(BindingContext.DECLARATION_TO_DESCRIPTOR, localDeclaration);
213                    JetType expectedType = functionDescriptor != null ? functionDescriptor.getReturnType() : null;
214    
215                    JetFlowInformationProvider providerForLocalDeclaration =
216                            new JetFlowInformationProvider(localDeclaration, trace, localDeclarationInstruction.getBody());
217    
218                    providerForLocalDeclaration.checkFunction(expectedType);
219                }
220            }
221        }
222    
223        public void checkDefiniteReturn(final @NotNull JetType expectedReturnType, @NotNull final UnreachableCode unreachableCode) {
224            assert subroutine instanceof JetDeclarationWithBody;
225            JetDeclarationWithBody function = (JetDeclarationWithBody) subroutine;
226    
227            if (!function.hasBody()) return;
228    
229            List<JetElement> returnedExpressions = Lists.newArrayList();
230            collectReturnExpressions(returnedExpressions);
231    
232            final boolean blockBody = function.hasBlockBody();
233    
234            final boolean[] noReturnError = new boolean[] { false };
235            for (JetElement returnedExpression : returnedExpressions) {
236                returnedExpression.accept(new JetVisitorVoid() {
237                    @Override
238                    public void visitReturnExpression(@NotNull JetReturnExpression expression) {
239                        if (!blockBody) {
240                            trace.report(RETURN_IN_FUNCTION_WITH_EXPRESSION_BODY.on(expression));
241                        }
242                    }
243    
244                    @Override
245                    public void visitJetElement(@NotNull JetElement element) {
246                        if (!(element instanceof JetExpression || element instanceof JetWhenCondition)) return;
247    
248                        if (blockBody && !noExpectedType(expectedReturnType)
249                                && !KotlinBuiltIns.isUnit(expectedReturnType)
250                                && !unreachableCode.getElements().contains(element)) {
251                            noReturnError[0] = true;
252                        }
253                    }
254                });
255            }
256            if (noReturnError[0]) {
257                trace.report(NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY.on(function));
258            }
259        }
260    
261        private void reportUnreachableCode(@NotNull UnreachableCode unreachableCode) {
262            for (JetElement element : unreachableCode.getElements()) {
263                trace.report(UNREACHABLE_CODE.on(element, unreachableCode.getUnreachableTextRanges(element)));
264                trace.record(BindingContext.UNREACHABLE_CODE, element, true);
265            }
266        }
267    
268        @NotNull
269        private UnreachableCode collectUnreachableCode() {
270            Set<JetElement> reachableElements = Sets.newHashSet();
271            Set<JetElement> unreachableElements = Sets.newHashSet();
272            for (Instruction instruction : pseudocode.getInstructionsIncludingDeadCode()) {
273                if (!(instruction instanceof JetElementInstruction)
274                        || instruction instanceof LoadUnitValueInstruction
275                        || instruction instanceof MergeInstruction
276                        || (instruction instanceof MagicInstruction && ((MagicInstruction) instruction).getSynthetic())) continue;
277    
278                JetElement element = ((JetElementInstruction) instruction).getElement();
279    
280                if (instruction instanceof JumpInstruction) {
281                    boolean isJumpElement = element instanceof JetBreakExpression
282                                            || element instanceof JetContinueExpression
283                                            || element instanceof JetReturnExpression
284                                            || element instanceof JetThrowExpression;
285                    if (!isJumpElement) continue;
286                }
287    
288                if (instruction.getDead()) {
289                    unreachableElements.add(element);
290                }
291                else {
292                    reachableElements.add(element);
293                }
294            }
295            return new UnreachableCodeImpl(reachableElements, unreachableElements);
296        }
297    
298    ////////////////////////////////////////////////////////////////////////////////
299    //  Uninitialized variables analysis
300    
301        public void markUninitializedVariables() {
302            final Collection<VariableDescriptor> varWithUninitializedErrorGenerated = Sets.newHashSet();
303            final Collection<VariableDescriptor> varWithValReassignErrorGenerated = Sets.newHashSet();
304            final boolean processClassOrObject = subroutine instanceof JetClassOrObject;
305    
306            PseudocodeVariablesData pseudocodeVariablesData = getPseudocodeVariablesData();
307            Map<Instruction, Edges<Map<VariableDescriptor, VariableInitState>>> initializers =
308                    pseudocodeVariablesData.getVariableInitializers();
309            final Set<VariableDescriptor> declaredVariables = pseudocodeVariablesData.getDeclaredVariables(pseudocode, true);
310            final LexicalScopeVariableInfo lexicalScopeVariableInfo = pseudocodeVariablesData.getLexicalScopeVariableInfo();
311    
312            final Map<Instruction, DiagnosticFactory<?>> reportedDiagnosticMap = Maps.newHashMap();
313    
314            PseudocodeTraverserPackage.traverse(
315                    pseudocode, FORWARD, initializers,
316                    new InstructionDataAnalyzeStrategy<Map<VariableDescriptor, VariableInitState>>() {
317                        @Override
318                        public void execute(
319                                @NotNull Instruction instruction,
320                                @Nullable Map<VariableDescriptor, VariableInitState> in,
321                                @Nullable Map<VariableDescriptor, VariableInitState> out
322                        ) {
323                            assert in != null && out != null;
324                            VariableInitContext ctxt =
325                                    new VariableInitContext(instruction, reportedDiagnosticMap, in, out, lexicalScopeVariableInfo);
326                            if (ctxt.variableDescriptor == null) return;
327                            if (instruction instanceof ReadValueInstruction) {
328                                JetElement element = ((ReadValueInstruction) instruction).getElement();
329                                boolean error = checkBackingField(ctxt, element);
330                                if (!error && declaredVariables.contains(ctxt.variableDescriptor)) {
331                                    checkIsInitialized(ctxt, element, varWithUninitializedErrorGenerated);
332                                }
333                                return;
334                            }
335                            if (!(instruction instanceof WriteValueInstruction)) return;
336                            JetElement element = ((WriteValueInstruction) instruction).getlValue();
337                            boolean error = checkBackingField(ctxt, element);
338                            if (!(element instanceof JetExpression)) return;
339                            if (!error) {
340                                error = checkValReassignment(ctxt, (JetExpression) element, varWithValReassignErrorGenerated);
341                            }
342                            if (!error && processClassOrObject) {
343                                error = checkAssignmentBeforeDeclaration(ctxt, (JetExpression) element);
344                            }
345                            if (!error && processClassOrObject) {
346                                checkInitializationUsingBackingField(ctxt, (JetExpression) element);
347                            }
348                        }
349                    }
350            );
351        }
352    
353        public void recordInitializedVariables() {
354            PseudocodeVariablesData pseudocodeVariablesData = getPseudocodeVariablesData();
355            Pseudocode pseudocode = pseudocodeVariablesData.getPseudocode();
356            Map<Instruction, Edges<Map<VariableDescriptor, VariableInitState>>> initializers =
357                    pseudocodeVariablesData.getVariableInitializers();
358            recordInitializedVariables(pseudocode, initializers);
359            for (LocalFunctionDeclarationInstruction instruction : pseudocode.getLocalDeclarations()) {
360                recordInitializedVariables(instruction.getBody(), initializers);
361            }
362        }
363    
364        private void checkIsInitialized(
365                @NotNull VariableInitContext ctxt,
366                @NotNull JetElement element,
367                @NotNull Collection<VariableDescriptor> varWithUninitializedErrorGenerated
368        ) {
369            if (!(element instanceof JetSimpleNameExpression)) return;
370    
371            boolean isInitialized = ctxt.exitInitState.isInitialized;
372            VariableDescriptor variableDescriptor = ctxt.variableDescriptor;
373            if (variableDescriptor instanceof PropertyDescriptor) {
374                if (!trace.get(BindingContext.BACKING_FIELD_REQUIRED, (PropertyDescriptor) variableDescriptor)) {
375                    isInitialized = true;
376                }
377            }
378            if (!isInitialized && !varWithUninitializedErrorGenerated.contains(variableDescriptor)) {
379                if (!(variableDescriptor instanceof PropertyDescriptor)) {
380                    varWithUninitializedErrorGenerated.add(variableDescriptor);
381                }
382                if (variableDescriptor instanceof ValueParameterDescriptor) {
383                    report(Errors.UNINITIALIZED_PARAMETER.on((JetSimpleNameExpression) element,
384                                                             (ValueParameterDescriptor) variableDescriptor), ctxt);
385                }
386                else {
387                    report(Errors.UNINITIALIZED_VARIABLE.on((JetSimpleNameExpression) element, variableDescriptor), ctxt);
388                }
389            }
390        }
391    
392        private boolean checkValReassignment(
393                @NotNull VariableInitContext ctxt,
394                @NotNull JetExpression expression,
395                @NotNull Collection<VariableDescriptor> varWithValReassignErrorGenerated
396        ) {
397            VariableDescriptor variableDescriptor = ctxt.variableDescriptor;
398            if (JetPsiUtil.isBackingFieldReference(expression) && variableDescriptor instanceof PropertyDescriptor) {
399                PropertyDescriptor propertyDescriptor = (PropertyDescriptor) variableDescriptor;
400                JetPropertyAccessor accessor = PsiTreeUtil.getParentOfType(expression, JetPropertyAccessor.class);
401                if (accessor != null) {
402                    DeclarationDescriptor accessorDescriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, accessor);
403                    if (propertyDescriptor.getGetter() == accessorDescriptor) {
404                        //val can be reassigned through backing field inside its own getter
405                        return false;
406                    }
407                }
408            }
409    
410            boolean isInitializedNotHere = ctxt.enterInitState.isInitialized;
411            boolean hasBackingField = true;
412            if (variableDescriptor instanceof PropertyDescriptor) {
413                hasBackingField = trace.get(BindingContext.BACKING_FIELD_REQUIRED, (PropertyDescriptor) variableDescriptor);
414            }
415            if (variableDescriptor.isVar() && variableDescriptor instanceof PropertyDescriptor) {
416                DeclarationDescriptor descriptor = BindingContextUtils.getEnclosingDescriptor(trace.getBindingContext(), expression);
417                PropertySetterDescriptor setterDescriptor = ((PropertyDescriptor) variableDescriptor).getSetter();
418    
419                ResolvedCall<? extends CallableDescriptor> resolvedCall = CallUtilPackage.getResolvedCall(expression, trace.getBindingContext());
420                ReceiverValue receiverValue = ReceiverValue.IRRELEVANT_RECEIVER;
421                if (resolvedCall != null) {
422                    receiverValue = ExpressionTypingUtils
423                            .normalizeReceiverValueForVisibility(resolvedCall.getDispatchReceiver(), trace.getBindingContext());
424    
425                }
426                if (Visibilities.isVisible(receiverValue, variableDescriptor, descriptor) && setterDescriptor != null
427                        && !Visibilities.isVisible(receiverValue, setterDescriptor, descriptor)) {
428                    report(Errors.INVISIBLE_SETTER.on(expression, variableDescriptor, setterDescriptor.getVisibility(),
429                                                      variableDescriptor.getContainingDeclaration()), ctxt);
430                    return true;
431                }
432            }
433            if ((isInitializedNotHere || !hasBackingField) && !variableDescriptor.isVar()
434                    && !varWithValReassignErrorGenerated.contains(variableDescriptor)) {
435                boolean hasReassignMethodReturningUnit = false;
436                JetSimpleNameExpression operationReference = null;
437                PsiElement parent = expression.getParent();
438                if (parent instanceof JetBinaryExpression) {
439                    operationReference = ((JetBinaryExpression) parent).getOperationReference();
440                }
441                else if (parent instanceof JetUnaryExpression) {
442                    operationReference = ((JetUnaryExpression) parent).getOperationReference();
443                }
444                if (operationReference != null) {
445                    DeclarationDescriptor descriptor = trace.get(BindingContext.REFERENCE_TARGET, operationReference);
446                    if (descriptor instanceof FunctionDescriptor) {
447                        if (KotlinBuiltIns.isUnit(((FunctionDescriptor) descriptor).getReturnType())) {
448                            hasReassignMethodReturningUnit = true;
449                        }
450                    }
451                    if (descriptor == null) {
452                        Collection<? extends DeclarationDescriptor> descriptors =
453                                trace.get(BindingContext.AMBIGUOUS_REFERENCE_TARGET, operationReference);
454                        if (descriptors != null) {
455                            for (DeclarationDescriptor referenceDescriptor : descriptors) {
456                                if (KotlinBuiltIns.isUnit(((FunctionDescriptor) referenceDescriptor).getReturnType())) {
457                                    hasReassignMethodReturningUnit = true;
458                                }
459                            }
460                        }
461                    }
462                }
463                if (!hasReassignMethodReturningUnit) {
464                    varWithValReassignErrorGenerated.add(variableDescriptor);
465                    report(Errors.VAL_REASSIGNMENT.on(expression, variableDescriptor), ctxt);
466                    return true;
467                }
468            }
469            return false;
470        }
471    
472        private boolean checkAssignmentBeforeDeclaration(@NotNull VariableInitContext ctxt, @NotNull JetExpression expression) {
473            if (!ctxt.enterInitState.isDeclared && !ctxt.exitInitState.isDeclared
474                    && !ctxt.enterInitState.isInitialized && ctxt.exitInitState.isInitialized) {
475                report(Errors.INITIALIZATION_BEFORE_DECLARATION.on(expression, ctxt.variableDescriptor), ctxt);
476                return true;
477            }
478            return false;
479        }
480    
481        private boolean checkInitializationUsingBackingField(@NotNull VariableInitContext ctxt, @NotNull JetExpression expression) {
482            VariableDescriptor variableDescriptor = ctxt.variableDescriptor;
483            if (variableDescriptor instanceof PropertyDescriptor && !ctxt.enterInitState.isInitialized && ctxt.exitInitState.isInitialized) {
484                if (!variableDescriptor.isVar()) return false;
485                if (!trace.get(BindingContext.BACKING_FIELD_REQUIRED, (PropertyDescriptor) variableDescriptor)) return false;
486                PsiElement property = DescriptorToSourceUtils.descriptorToDeclaration(variableDescriptor);
487                assert property instanceof JetProperty;
488                if (((PropertyDescriptor) variableDescriptor).getModality() == Modality.FINAL && ((JetProperty) property).getSetter() == null) {
489                    return false;
490                }
491                JetExpression variable = expression;
492                if (expression instanceof JetDotQualifiedExpression) {
493                    if (((JetDotQualifiedExpression) expression).getReceiverExpression() instanceof JetThisExpression) {
494                        variable = ((JetDotQualifiedExpression) expression).getSelectorExpression();
495                    }
496                }
497                if (variable instanceof JetSimpleNameExpression) {
498                    JetSimpleNameExpression simpleNameExpression = (JetSimpleNameExpression) variable;
499                    if (simpleNameExpression.getReferencedNameElementType() != JetTokens.FIELD_IDENTIFIER) {
500                        if (((PropertyDescriptor) variableDescriptor).getModality() != Modality.FINAL) {
501                            report(Errors.INITIALIZATION_USING_BACKING_FIELD_OPEN_SETTER.on(expression, variableDescriptor), ctxt);
502                        }
503                        else {
504                            report(Errors.INITIALIZATION_USING_BACKING_FIELD_CUSTOM_SETTER.on(expression, variableDescriptor), ctxt);
505                        }
506                        return true;
507                    }
508                }
509            }
510            return false;
511        }
512    
513        private boolean checkBackingField(@NotNull VariableContext cxtx, @NotNull JetElement element) {
514            VariableDescriptor variableDescriptor = cxtx.variableDescriptor;
515            boolean[] error = new boolean[1];
516            if (!isCorrectBackingFieldReference(element, cxtx, error, true)) return false;
517            if (error[0]) return true;
518            if (!(variableDescriptor instanceof PropertyDescriptor)) {
519                report(Errors.NOT_PROPERTY_BACKING_FIELD.on(element), cxtx);
520                return true;
521            }
522            PsiElement property = DescriptorToSourceUtils.descriptorToDeclaration(variableDescriptor);
523            boolean insideSelfAccessors = PsiTreeUtil.isAncestor(property, element, false);
524            if (!trace.get(BindingContext.BACKING_FIELD_REQUIRED, (PropertyDescriptor) variableDescriptor) &&
525                    // not to generate error in accessors of abstract properties, there is one: declared accessor of abstract property
526                    !insideSelfAccessors) {
527    
528                if (((PropertyDescriptor) variableDescriptor).getModality() == Modality.ABSTRACT) {
529                    report(NO_BACKING_FIELD_ABSTRACT_PROPERTY.on(element), cxtx);
530                }
531                else {
532                    report(NO_BACKING_FIELD_CUSTOM_ACCESSORS.on(element), cxtx);
533                }
534                return true;
535            }
536            if (insideSelfAccessors) return false;
537    
538            DeclarationDescriptor declarationDescriptor = BindingContextUtils.getEnclosingDescriptor(trace.getBindingContext(), element);
539    
540            DeclarationDescriptor containingDeclaration = variableDescriptor.getContainingDeclaration();
541            if ((containingDeclaration instanceof ClassDescriptor)
542                    && DescriptorUtils.isAncestor(containingDeclaration, declarationDescriptor, false)) {
543                return false;
544            }
545            report(Errors.INACCESSIBLE_BACKING_FIELD.on(element), cxtx);
546            return true;
547        }
548    
549        private boolean isCorrectBackingFieldReference(
550                @Nullable JetElement element,
551                VariableContext ctxt,
552                boolean[] error,
553                boolean reportError
554        ) {
555            error[0] = false;
556            if (JetPsiUtil.isBackingFieldReference(element)) {
557                return true;
558            }
559            if (element instanceof JetDotQualifiedExpression && isCorrectBackingFieldReference(
560                    ((JetDotQualifiedExpression) element).getSelectorExpression(), ctxt, error, false)) {
561                if (((JetDotQualifiedExpression) element).getReceiverExpression() instanceof JetThisExpression) {
562                    return true;
563                }
564                error[0] = true;
565                if (reportError) {
566                    report(Errors.INACCESSIBLE_BACKING_FIELD.on(element), ctxt);
567                }
568            }
569            return false;
570        }
571    
572        private void recordInitializedVariables(
573                @NotNull Pseudocode pseudocode,
574                @NotNull Map<Instruction, Edges<Map<VariableDescriptor, PseudocodeVariablesData.VariableInitState>>> initializersMap
575        ) {
576            Edges<Map<VariableDescriptor, VariableInitState>> initializers = initializersMap.get(pseudocode.getExitInstruction());
577            Set<VariableDescriptor> declaredVariables = getPseudocodeVariablesData().getDeclaredVariables(pseudocode, false);
578            for (VariableDescriptor variable : declaredVariables) {
579                if (variable instanceof PropertyDescriptor) {
580                    PseudocodeVariablesData.VariableInitState variableInitState = initializers.getIncoming().get(variable);
581                    if (variableInitState == null) return;
582                    trace.record(BindingContext.IS_INITIALIZED, (PropertyDescriptor) variable, variableInitState.isInitialized);
583                }
584            }
585        }
586    
587    ////////////////////////////////////////////////////////////////////////////////
588    //  "Unused variable" & "unused value" analyses
589    
590        public void markUnusedVariables() {
591            final PseudocodeVariablesData pseudocodeVariablesData = getPseudocodeVariablesData();
592            Map<Instruction, Edges<Map<VariableDescriptor, VariableUseState>>> variableStatusData =
593                    pseudocodeVariablesData.getVariableUseStatusData();
594            final Map<Instruction, DiagnosticFactory<?>> reportedDiagnosticMap = Maps.newHashMap();
595            InstructionDataAnalyzeStrategy<Map<VariableDescriptor, VariableUseState>> variableStatusAnalyzeStrategy =
596                    new InstructionDataAnalyzeStrategy<Map<VariableDescriptor, VariableUseState>>() {
597                        @Override
598                        public void execute(
599                                @NotNull Instruction instruction,
600                                @Nullable Map<VariableDescriptor, VariableUseState> in,
601                                @Nullable Map<VariableDescriptor, VariableUseState> out
602                        ) {
603    
604                            assert in != null && out != null;
605                            VariableContext ctxt = new VariableUseContext(instruction, reportedDiagnosticMap, in, out);
606                            Set<VariableDescriptor> declaredVariables =
607                                    pseudocodeVariablesData.getDeclaredVariables(instruction.getOwner(), false);
608                            VariableDescriptor variableDescriptor = PseudocodeUtil.extractVariableDescriptorIfAny(
609                                    instruction, false, trace.getBindingContext());
610                            if (variableDescriptor == null || !declaredVariables.contains(variableDescriptor)
611                                    || !ExpressionTypingUtils.isLocal(variableDescriptor.getContainingDeclaration(), variableDescriptor)) {
612                                return;
613                            }
614                            PseudocodeVariablesData.VariableUseState variableUseState = in.get(variableDescriptor);
615                            if (instruction instanceof WriteValueInstruction) {
616                                if (trace.get(CAPTURED_IN_CLOSURE, variableDescriptor) != null) return;
617                                JetElement element = ((WriteValueInstruction) instruction).getElement();
618                                if (variableUseState != READ) {
619                                    if (element instanceof JetBinaryExpression &&
620                                        ((JetBinaryExpression) element).getOperationToken() == JetTokens.EQ) {
621                                        JetExpression right = ((JetBinaryExpression) element).getRight();
622                                        if (right != null) {
623                                            report(Errors.UNUSED_VALUE.on(right, right, variableDescriptor), ctxt);
624                                        }
625                                    }
626                                    else if (element instanceof JetPostfixExpression) {
627                                        IElementType operationToken =
628                                                ((JetPostfixExpression) element).getOperationReference().getReferencedNameElementType();
629                                        if (operationToken == JetTokens.PLUSPLUS || operationToken == JetTokens.MINUSMINUS) {
630                                            report(Errors.UNUSED_CHANGED_VALUE.on(element, element), ctxt);
631                                        }
632                                    }
633                                }
634                            }
635                            else if (instruction instanceof VariableDeclarationInstruction) {
636                                JetDeclaration element = ((VariableDeclarationInstruction) instruction).getVariableDeclarationElement();
637                                if (!(element instanceof JetNamedDeclaration)) return;
638                                PsiElement nameIdentifier = ((JetNamedDeclaration) element).getNameIdentifier();
639                                if (nameIdentifier == null) return;
640                                if (!VariableUseState.isUsed(variableUseState)) {
641                                    if (JetPsiUtil.isVariableNotParameterDeclaration(element)) {
642                                        report(Errors.UNUSED_VARIABLE.on((JetNamedDeclaration) element, variableDescriptor), ctxt);
643                                    }
644                                    else if (element instanceof JetParameter) {
645                                        PsiElement psiElement = element.getParent().getParent();
646                                        if (psiElement instanceof JetFunction) {
647                                            MainFunctionDetector mainFunctionDetector = new MainFunctionDetector(trace.getBindingContext());
648                                            boolean isMain = (psiElement instanceof JetNamedFunction) && mainFunctionDetector.isMain((JetNamedFunction) psiElement);
649                                            if (psiElement instanceof JetFunctionLiteral) return;
650                                            DeclarationDescriptor descriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, psiElement);
651                                            assert descriptor instanceof FunctionDescriptor : psiElement.getText();
652                                            FunctionDescriptor functionDescriptor = (FunctionDescriptor) descriptor;
653                                            if (!isMain && !functionDescriptor.getModality().isOverridable()
654                                                    && functionDescriptor.getOverriddenDescriptors().isEmpty()) {
655                                                report(Errors.UNUSED_PARAMETER.on((JetParameter) element, variableDescriptor), ctxt);
656                                            }
657                                        }
658                                    }
659                                }
660                                else if (variableUseState == ONLY_WRITTEN_NEVER_READ && JetPsiUtil.isVariableNotParameterDeclaration(element)) {
661                                    report(Errors.ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE.on((JetNamedDeclaration) element, variableDescriptor), ctxt);
662                                }
663                                else if (variableUseState == WRITTEN_AFTER_READ && element instanceof JetVariableDeclaration) {
664                                    if (element instanceof JetProperty) {
665                                        JetExpression initializer = ((JetProperty) element).getInitializer();
666                                        if (initializer != null) {
667                                            report(Errors.VARIABLE_WITH_REDUNDANT_INITIALIZER.on(initializer, variableDescriptor), ctxt);
668                                        }
669                                    }
670                                    else if (element instanceof JetMultiDeclarationEntry) {
671                                        report(VARIABLE_WITH_REDUNDANT_INITIALIZER.on(element, variableDescriptor), ctxt);
672                                    }
673                                }
674                            }
675                        }
676                    };
677            PseudocodeTraverserPackage.traverse(pseudocode, TraversalOrder.BACKWARD, variableStatusData, variableStatusAnalyzeStrategy);
678        }
679    
680    ////////////////////////////////////////////////////////////////////////////////
681    //  "Unused expressions" in block
682    
683        public void markUnusedExpressions() {
684            final Map<Instruction, DiagnosticFactory<?>> reportedDiagnosticMap = Maps.newHashMap();
685            PseudocodeTraverserPackage.traverse(
686                    pseudocode, FORWARD, new JetFlowInformationProvider.FunctionVoid1<Instruction>() {
687                        @Override
688                        public void execute(@NotNull Instruction instruction) {
689                            if (!(instruction instanceof JetElementInstruction)) return;
690    
691                            JetElement element = ((JetElementInstruction)instruction).getElement();
692                            if (!(element instanceof JetExpression)) return;
693    
694                            if (BindingContextUtilPackage.isUsedAsStatement((JetExpression) element, trace.getBindingContext())
695                                    && PseudocodePackage.getSideEffectFree(instruction)) {
696                                VariableContext ctxt = new VariableContext(instruction, reportedDiagnosticMap);
697                                report(
698                                        element instanceof JetFunctionLiteralExpression
699                                            ? Errors.UNUSED_FUNCTION_LITERAL.on((JetFunctionLiteralExpression) element)
700                                            : Errors.UNUSED_EXPRESSION.on(element),
701                                        ctxt
702                                );
703                            }
704                        }
705                    }
706            );
707        }
708    
709    ////////////////////////////////////////////////////////////////////////////////
710    // Statements
711    
712        public void markStatements() {
713            PseudocodeTraverserPackage.traverse(
714                    pseudocode, FORWARD, new JetFlowInformationProvider.FunctionVoid1<Instruction>() {
715                        @Override
716                        public void execute(@NotNull Instruction instruction) {
717                            PseudoValue value = instruction instanceof InstructionWithValue
718                                                ? ((InstructionWithValue) instruction).getOutputValue()
719                                                : null;
720                            Pseudocode pseudocode = instruction.getOwner();
721                            boolean isUsedAsExpression = !pseudocode.getUsages(value).isEmpty();
722                            for (JetElement element : pseudocode.getValueElements(value)) {
723                                trace.record(BindingContext.USED_AS_EXPRESSION, element, isUsedAsExpression);
724                            }
725                        }
726                    }
727            );
728        }
729    
730        public void markWhenWithoutElse() {
731            PseudocodeTraverserPackage.traverse(
732                    pseudocode, FORWARD, new JetFlowInformationProvider.FunctionVoid1<Instruction>() {
733                        @Override
734                        public void execute(@NotNull Instruction instruction) {
735                            PseudoValue value = instruction instanceof InstructionWithValue
736                                                ? ((InstructionWithValue) instruction).getOutputValue()
737                                                : null;
738                            for (JetElement element : instruction.getOwner().getValueElements(value)) {
739                                if (element instanceof JetWhenExpression) {
740                                    JetWhenExpression whenExpression = (JetWhenExpression) element;
741                                    if (whenExpression.getElseExpression() == null && WhenChecker.mustHaveElse(whenExpression, trace)) {
742                                        trace.report(NO_ELSE_IN_WHEN.on(whenExpression));
743                                    }
744                                }
745                            }
746                        }
747                    }
748            );
749        }
750    
751    ////////////////////////////////////////////////////////////////////////////////
752    // Tail calls
753    
754        public void markTailCalls() {
755            final DeclarationDescriptor subroutineDescriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, subroutine);
756            if (!(subroutineDescriptor instanceof FunctionDescriptor)) return;
757            if (!KotlinBuiltIns.isTailRecursive(subroutineDescriptor)) return;
758    
759            // finally blocks are copied which leads to multiple diagnostics reported on one instruction
760            class KindAndCall {
761                TailRecursionKind kind;
762                ResolvedCall<?> call;
763    
764                KindAndCall(TailRecursionKind kind, ResolvedCall<?> call) {
765                    this.kind = kind;
766                    this.call = call;
767                }
768            }
769            final Map<JetElement, KindAndCall> calls = new HashMap<JetElement, KindAndCall>();
770            PseudocodeTraverserPackage.traverse(
771                    pseudocode,
772                    FORWARD,
773                    new FunctionVoid1<Instruction>() {
774                        public void execute(@NotNull Instruction instruction) {
775                            if (!(instruction instanceof CallInstruction)) return;
776                            CallInstruction callInstruction = (CallInstruction) instruction;
777    
778                            ResolvedCall<?> resolvedCall = getResolvedCall(callInstruction.getElement(), trace.getBindingContext());
779                            if (resolvedCall == null) return;
780    
781                            // is this a recursive call?
782                            CallableDescriptor functionDescriptor = resolvedCall.getResultingDescriptor();
783                            if (!functionDescriptor.getOriginal().equals(subroutineDescriptor)) return;
784    
785                            JetElement element = callInstruction.getElement();
786                            //noinspection unchecked
787                            JetExpression parent = PsiTreeUtil.getParentOfType(
788                                    element,
789                                    JetTryExpression.class, JetFunction.class, JetClassInitializer.class
790                            );
791    
792                            if (parent instanceof JetTryExpression) {
793                                // We do not support tail calls Collections.singletonMap() try-catch-finally, for simplicity of the mental model
794                                // very few cases there would be real tail-calls, and it's often not so easy for the user to see why
795                                calls.put(element, new KindAndCall(IN_TRY, resolvedCall));
796                                return;
797                            }
798    
799                            boolean isTail = PseudocodeTraverserPackage.traverseFollowingInstructions(
800                                    callInstruction,
801                                    new HashSet<Instruction>(),
802                                    FORWARD,
803                                    new TailRecursionDetector(subroutine, callInstruction)
804                            );
805    
806                            boolean sameDispatchReceiver = sameDispatchReceiver(resolvedCall);
807    
808                            TailRecursionKind kind = isTail && sameDispatchReceiver ? TAIL_CALL : NON_TAIL;
809    
810                            KindAndCall kindAndCall = calls.get(element);
811                            calls.put(element,
812                                      new KindAndCall(
813                                              combineKinds(kind, kindAndCall == null ? null : kindAndCall.kind),
814                                              resolvedCall
815                                      )
816                            );
817                        }
818                    }
819            );
820            boolean hasTailCalls = false;
821            for (Map.Entry<JetElement, KindAndCall> entry : calls.entrySet()) {
822                JetElement element = entry.getKey();
823                KindAndCall kindAndCall = entry.getValue();
824                switch (kindAndCall.kind) {
825                    case TAIL_CALL:
826                        trace.record(TAIL_RECURSION_CALL, kindAndCall.call, TailRecursionKind.TAIL_CALL);
827                        hasTailCalls = true;
828                        break;
829                    case IN_TRY:
830                        trace.report(Errors.TAIL_RECURSION_IN_TRY_IS_NOT_SUPPORTED.on(element));
831                        break;
832                    case NON_TAIL:
833                        trace.report(Errors.NON_TAIL_RECURSIVE_CALL.on(element));
834                        break;
835                }
836            }
837    
838            if (!hasTailCalls) {
839                trace.report(Errors.NO_TAIL_CALLS_FOUND.on((JetNamedFunction) subroutine));
840            }
841        }
842    
843        private boolean sameDispatchReceiver(ResolvedCall<?> resolvedCall) {
844            // A tail call is not allowed to change dispatch receiver
845            //   class C {
846            //       fun foo(other: C) {
847            //           other.foo(this) // not a tail call
848            //       }
849            //   }
850            ReceiverParameterDescriptor dispatchReceiverParameter = resolvedCall.getResultingDescriptor().getDispatchReceiverParameter();
851            ReceiverValue dispatchReceiverValue = resolvedCall.getDispatchReceiver();
852            if (dispatchReceiverParameter == null || !dispatchReceiverValue.exists()) return true;
853    
854            DeclarationDescriptor classDescriptor = null;
855            if (dispatchReceiverValue instanceof ThisReceiver) {
856                // foo() -- implicit receiver
857                classDescriptor = ((ThisReceiver) dispatchReceiverValue).getDeclarationDescriptor();
858            }
859            else if (dispatchReceiverValue instanceof ExpressionReceiver) {
860                JetExpression expression = JetPsiUtil.deparenthesize(((ExpressionReceiver) dispatchReceiverValue).getExpression());
861                if (expression instanceof JetThisExpression) {
862                    // this.foo() -- explicit receiver
863                    JetThisExpression thisExpression = (JetThisExpression) expression;
864                    classDescriptor = trace.get(BindingContext.REFERENCE_TARGET, thisExpression.getInstanceReference());
865                }
866            }
867            return dispatchReceiverParameter.getContainingDeclaration() == classDescriptor;
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    }