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