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