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