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