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 org.jetbrains.annotations.NotNull;
026    import org.jetbrains.annotations.Nullable;
027    import org.jetbrains.jet.lang.cfg.pseudocode.*;
028    import org.jetbrains.jet.lang.cfg.PseudocodeTraverser.*;
029    import org.jetbrains.jet.lang.cfg.PseudocodeVariablesData.VariableInitState;
030    import org.jetbrains.jet.lang.cfg.PseudocodeVariablesData.VariableUseState;
031    import org.jetbrains.jet.lang.descriptors.*;
032    import org.jetbrains.jet.lang.diagnostics.AbstractDiagnosticFactory;
033    import org.jetbrains.jet.lang.diagnostics.Diagnostic;
034    import org.jetbrains.jet.lang.diagnostics.Errors;
035    import org.jetbrains.jet.lang.psi.*;
036    import org.jetbrains.jet.lang.resolve.BindingContext;
037    import org.jetbrains.jet.lang.resolve.BindingContextUtils;
038    import org.jetbrains.jet.lang.resolve.BindingTrace;
039    import org.jetbrains.jet.lang.resolve.DescriptorUtils;
040    import org.jetbrains.jet.lang.types.JetType;
041    import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
042    import org.jetbrains.jet.lexer.JetTokens;
043    import org.jetbrains.jet.plugin.JetMainDetector;
044    
045    import java.util.*;
046    
047    import static org.jetbrains.jet.lang.cfg.PseudocodeTraverser.TraversalOrder.BACKWARD;
048    import static org.jetbrains.jet.lang.cfg.PseudocodeTraverser.TraversalOrder.FORWARD;
049    import static org.jetbrains.jet.lang.cfg.PseudocodeVariablesData.VariableUseState.*;
050    import static org.jetbrains.jet.lang.diagnostics.Errors.*;
051    import static org.jetbrains.jet.lang.resolve.BindingContext.CAPTURED_IN_CLOSURE;
052    import static org.jetbrains.jet.lang.types.TypeUtils.NO_EXPECTED_TYPE;
053    import static org.jetbrains.jet.lang.types.TypeUtils.noExpectedType;
054    
055    public class JetFlowInformationProvider {
056    
057        private final JetElement subroutine;
058        private final Pseudocode pseudocode;
059        private final PseudocodeVariablesData pseudocodeVariablesData;
060        private BindingTrace trace;
061    
062        public JetFlowInformationProvider(
063                @NotNull JetElement declaration,
064                @NotNull BindingTrace trace) {
065    
066            subroutine = declaration;
067            this.trace = trace;
068            pseudocode = new JetControlFlowProcessor(trace).generatePseudocode(declaration);
069            pseudocodeVariablesData = new PseudocodeVariablesData(pseudocode, trace.getBindingContext());
070        }
071    
072        private void collectReturnExpressions(@NotNull final Collection<JetElement> returnedExpressions) {
073            final Set<Instruction> instructions = Sets.newHashSet(pseudocode.getInstructions());
074            SubroutineExitInstruction exitInstruction = pseudocode.getExitInstruction();
075            for (Instruction previousInstruction : exitInstruction.getPreviousInstructions()) {
076                previousInstruction.accept(new InstructionVisitor() {
077                    @Override
078                    public void visitReturnValue(ReturnValueInstruction instruction) {
079                        if (instructions.contains(instruction)) { //exclude non-local return expressions
080                            returnedExpressions.add(instruction.getElement());
081                        }
082                    }
083    
084                    @Override
085                    public void visitReturnNoValue(ReturnNoValueInstruction instruction) {
086                        if (instructions.contains(instruction)) {
087                            returnedExpressions.add(instruction.getElement());
088                        }
089                    }
090    
091    
092                    @Override
093                    public void visitJump(AbstractJumpInstruction instruction) {
094                        // Nothing
095                    }
096    
097                    @Override
098                    public void visitUnconditionalJump(UnconditionalJumpInstruction instruction) {
099                        redirectToPrevInstructions(instruction);
100                    }
101    
102                    private void redirectToPrevInstructions(Instruction instruction) {
103                        for (Instruction previousInstruction : instruction.getPreviousInstructions()) {
104                            previousInstruction.accept(this);
105                        }
106                    }
107    
108                    @Override
109                    public void visitNondeterministicJump(NondeterministicJumpInstruction instruction) {
110                        redirectToPrevInstructions(instruction);
111                    }
112    
113                    @Override
114                    public void visitInstruction(Instruction instruction) {
115                        if (instruction instanceof JetElementInstruction) {
116                            JetElementInstruction elementInstruction = (JetElementInstruction) instruction;
117                            returnedExpressions.add(elementInstruction.getElement());
118                        }
119                        else {
120                            throw new IllegalStateException(instruction + " precedes the exit point");
121                        }
122                    }
123                });
124            }
125        }
126    
127        public void checkDefiniteReturn(final @NotNull JetType expectedReturnType) {
128            assert subroutine instanceof JetDeclarationWithBody;
129            JetDeclarationWithBody function = (JetDeclarationWithBody) subroutine;
130    
131            JetExpression bodyExpression = function.getBodyExpression();
132            if (bodyExpression == null) return;
133    
134            List<JetElement> returnedExpressions = Lists.newArrayList();
135            collectReturnExpressions(returnedExpressions);
136    
137            final boolean blockBody = function.hasBlockBody();
138    
139            final Set<JetElement> rootUnreachableElements = collectUnreachableCode();
140            for (JetElement element : rootUnreachableElements) {
141                trace.report(UNREACHABLE_CODE.on(element));
142            }
143    
144            final boolean[] noReturnError = new boolean[] { false };
145            for (JetElement returnedExpression : returnedExpressions) {
146                returnedExpression.accept(new JetVisitorVoid() {
147                    @Override
148                    public void visitReturnExpression(JetReturnExpression expression) {
149                        if (!blockBody) {
150                            trace.report(RETURN_IN_FUNCTION_WITH_EXPRESSION_BODY.on(expression));
151                        }
152                    }
153    
154                    @Override
155                    public void visitExpression(JetExpression expression) {
156                        if (blockBody && !noExpectedType(expectedReturnType) && !KotlinBuiltIns.getInstance().isUnit(expectedReturnType) && !rootUnreachableElements.contains(expression)) {
157                            noReturnError[0] = true;
158                        }
159                    }
160                });
161            }
162            if (noReturnError[0]) {
163                trace.report(NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY.on(function));
164            }
165        }
166    
167        private Set<JetElement> collectUnreachableCode() {
168            Collection<JetElement> unreachableElements = Lists.newArrayList();
169            for (Instruction deadInstruction : pseudocode.getDeadInstructions()) {
170                if (deadInstruction instanceof JetElementInstruction &&
171                    !(deadInstruction instanceof ReadUnitValueInstruction)) {
172                    unreachableElements.add(((JetElementInstruction) deadInstruction).getElement());
173                }
174            }
175            // This is needed in order to highlight only '1 < 2' and not '1', '<' and '2' as well
176            return JetPsiUtil.findRootExpressions(unreachableElements);
177        }
178    
179    ////////////////////////////////////////////////////////////////////////////////
180    //  Uninitialized variables analysis
181    
182        public void markUninitializedVariables() {
183            final Collection<VariableDescriptor> varWithUninitializedErrorGenerated = Sets.newHashSet();
184            final Collection<VariableDescriptor> varWithValReassignErrorGenerated = Sets.newHashSet();
185            final boolean processClassOrObject = subroutine instanceof JetClassOrObject;
186    
187            Map<Instruction, Edges<Map<VariableDescriptor,VariableInitState>>> initializers = pseudocodeVariablesData.getVariableInitializers();
188            final Set<VariableDescriptor> declaredVariables = pseudocodeVariablesData.getDeclaredVariables(pseudocode, true);
189    
190            final Map<Instruction, AbstractDiagnosticFactory> reportedDiagnosticMap = Maps.newHashMap();
191    
192            PseudocodeTraverser.traverse(pseudocode, FORWARD, initializers, new InstructionDataAnalyzeStrategy<Map<VariableDescriptor, PseudocodeVariablesData.VariableInitState>>() {
193                @Override
194                public void execute(@NotNull Instruction instruction,
195                        @Nullable Map<VariableDescriptor, VariableInitState> in,
196                        @Nullable Map<VariableDescriptor, VariableInitState> out) {
197                    assert in != null && out != null;
198                    VariableInitContext ctxt = new VariableInitContext(instruction, reportedDiagnosticMap, in, out);
199                    if (ctxt.variableDescriptor == null) return;
200                    if (instruction instanceof ReadValueInstruction) {
201                        JetElement element = ((ReadValueInstruction) instruction).getElement();
202                        boolean error = checkBackingField(ctxt, element);
203                        if (!error && declaredVariables.contains(ctxt.variableDescriptor)) {
204                            checkIsInitialized(ctxt, element, varWithUninitializedErrorGenerated);
205                        }
206                        return;
207                    }
208                    if (!(instruction instanceof WriteValueInstruction)) return;
209                    JetElement element = ((WriteValueInstruction) instruction).getlValue();
210                    boolean error = checkBackingField(ctxt, element);
211                    if (!(element instanceof JetExpression)) return;
212                    if (!error) {
213                        error = checkValReassignment(ctxt, (JetExpression) element, varWithValReassignErrorGenerated);
214                    }
215                    if (!error && processClassOrObject) {
216                        error = checkAssignmentBeforeDeclaration(ctxt, (JetExpression) element);
217                    }
218                    if (!error && processClassOrObject) {
219                        checkInitializationUsingBackingField(ctxt, (JetExpression) element);
220                    }
221                }
222            });
223        }
224    
225        public void recordInitializedVariables() {
226            Pseudocode pseudocode = pseudocodeVariablesData.getPseudocode();
227            Map<Instruction, Edges<Map<VariableDescriptor,VariableInitState>>> initializers = pseudocodeVariablesData.getVariableInitializers();
228            recordInitializedVariables(pseudocode, initializers);
229            for (LocalDeclarationInstruction instruction : pseudocode.getLocalDeclarations()) {
230                recordInitializedVariables(instruction.getBody(), initializers);
231            }
232        }
233    
234        private void checkIsInitialized(
235                @NotNull VariableInitContext ctxt,
236                @NotNull JetElement element,
237                @NotNull Collection<VariableDescriptor> varWithUninitializedErrorGenerated
238        ) {
239            if (!(element instanceof JetSimpleNameExpression)) return;
240    
241            boolean isInitialized = ctxt.exitInitState.isInitialized;
242            VariableDescriptor variableDescriptor = ctxt.variableDescriptor;
243            if (variableDescriptor instanceof PropertyDescriptor) {
244                if (!trace.get(BindingContext.BACKING_FIELD_REQUIRED, (PropertyDescriptor) variableDescriptor)) {
245                    isInitialized = true;
246                }
247            }
248            if (!isInitialized && !varWithUninitializedErrorGenerated.contains(variableDescriptor)) {
249                if (!(variableDescriptor instanceof PropertyDescriptor)) {
250                    varWithUninitializedErrorGenerated.add(variableDescriptor);
251                }
252                if (variableDescriptor instanceof ValueParameterDescriptor) {
253                    report(Errors.UNINITIALIZED_PARAMETER.on((JetSimpleNameExpression) element,
254                                                             (ValueParameterDescriptor) variableDescriptor), ctxt);
255                }
256                else {
257                    report(Errors.UNINITIALIZED_VARIABLE.on((JetSimpleNameExpression) element, variableDescriptor), ctxt);
258                }
259            }
260        }
261    
262        private boolean checkValReassignment(
263                @NotNull VariableInitContext ctxt,
264                @NotNull JetExpression expression,
265                @NotNull Collection<VariableDescriptor> varWithValReassignErrorGenerated
266        ) {
267            VariableDescriptor variableDescriptor = ctxt.variableDescriptor;
268            if (JetPsiUtil.isBackingFieldReference(expression) && variableDescriptor instanceof PropertyDescriptor) {
269                PropertyDescriptor propertyDescriptor = (PropertyDescriptor) variableDescriptor;
270                JetPropertyAccessor accessor = PsiTreeUtil.getParentOfType(expression, JetPropertyAccessor.class);
271                if (accessor != null) {
272                    DeclarationDescriptor accessorDescriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, accessor);
273                    if (propertyDescriptor.getGetter() == accessorDescriptor) {
274                        //val can be reassigned through backing field inside its own getter
275                        return false;
276                    }
277                }
278            }
279    
280            boolean isInitializedNotHere = ctxt.enterInitState.isInitialized;
281            boolean hasBackingField = true;
282            if (variableDescriptor instanceof PropertyDescriptor) {
283                hasBackingField = trace.get(BindingContext.BACKING_FIELD_REQUIRED, (PropertyDescriptor) variableDescriptor);
284            }
285            if (variableDescriptor.isVar() && variableDescriptor instanceof PropertyDescriptor) {
286                DeclarationDescriptor descriptor = BindingContextUtils.getEnclosingDescriptor(trace.getBindingContext(), expression);
287                PropertySetterDescriptor setterDescriptor = ((PropertyDescriptor) variableDescriptor).getSetter();
288                if (Visibilities.isVisible(variableDescriptor, descriptor) && !Visibilities.isVisible(setterDescriptor, descriptor) && setterDescriptor != null) {
289                    report(Errors.INVISIBLE_SETTER.on(expression, variableDescriptor, setterDescriptor.getVisibility(),
290                                                      variableDescriptor.getContainingDeclaration()), ctxt);
291                    return true;
292                }
293            }
294            if ((isInitializedNotHere || !hasBackingField) && !variableDescriptor.isVar() && !varWithValReassignErrorGenerated.contains(variableDescriptor)) {
295                boolean hasReassignMethodReturningUnit = false;
296                JetSimpleNameExpression operationReference = null;
297                PsiElement parent = expression.getParent();
298                if (parent instanceof JetBinaryExpression) {
299                    operationReference = ((JetBinaryExpression) parent).getOperationReference();
300                }
301                else if (parent instanceof JetUnaryExpression) {
302                    operationReference = ((JetUnaryExpression) parent).getOperationReference();
303                }
304                if (operationReference != null) {
305                    DeclarationDescriptor descriptor = trace.get(BindingContext.REFERENCE_TARGET, operationReference);
306                    if (descriptor instanceof FunctionDescriptor) {
307                        if (KotlinBuiltIns.getInstance().isUnit(((FunctionDescriptor) descriptor).getReturnType())) {
308                            hasReassignMethodReturningUnit = true;
309                        }
310                    }
311                    if (descriptor == null) {
312                        Collection<? extends DeclarationDescriptor> descriptors = trace.get(BindingContext.AMBIGUOUS_REFERENCE_TARGET, operationReference);
313                        if (descriptors != null) {
314                            for (DeclarationDescriptor referenceDescriptor : descriptors) {
315                                if (KotlinBuiltIns.getInstance().isUnit(((FunctionDescriptor) referenceDescriptor).getReturnType())) {
316                                    hasReassignMethodReturningUnit = true;
317                                }
318                            }
319                        }
320                    }
321                }
322                if (!hasReassignMethodReturningUnit) {
323                    varWithValReassignErrorGenerated.add(variableDescriptor);
324                    report(Errors.VAL_REASSIGNMENT.on(expression, variableDescriptor), ctxt);
325                    return true;
326                }
327            }
328            return false;
329        }
330    
331        private boolean checkAssignmentBeforeDeclaration(@NotNull VariableInitContext ctxt, @NotNull JetExpression expression) {
332            if (!ctxt.enterInitState.isDeclared && !ctxt.exitInitState.isDeclared && !ctxt.enterInitState.isInitialized && ctxt.exitInitState.isInitialized) {
333                report(Errors.INITIALIZATION_BEFORE_DECLARATION.on(expression, ctxt.variableDescriptor), ctxt);
334                return true;
335            }
336            return false;
337        }
338    
339        private boolean checkInitializationUsingBackingField(@NotNull VariableInitContext ctxt, @NotNull JetExpression expression) {
340            VariableDescriptor variableDescriptor = ctxt.variableDescriptor;
341            if (variableDescriptor instanceof PropertyDescriptor && !ctxt.enterInitState.isInitialized && ctxt.exitInitState.isInitialized) {
342                if (!variableDescriptor.isVar()) return false;
343                if (!trace.get(BindingContext.BACKING_FIELD_REQUIRED, (PropertyDescriptor) variableDescriptor)) return false;
344                PsiElement property = BindingContextUtils.descriptorToDeclaration(trace.getBindingContext(), variableDescriptor);
345                assert property instanceof JetProperty;
346                if (((PropertyDescriptor) variableDescriptor).getModality() == Modality.FINAL && ((JetProperty) property).getSetter() == null) return false;
347                JetExpression variable = expression;
348                if (expression instanceof JetDotQualifiedExpression) {
349                    if (((JetDotQualifiedExpression) expression).getReceiverExpression() instanceof JetThisExpression) {
350                        variable = ((JetDotQualifiedExpression) expression).getSelectorExpression();
351                    }
352                }
353                if (variable instanceof JetSimpleNameExpression) {
354                    JetSimpleNameExpression simpleNameExpression = (JetSimpleNameExpression) variable;
355                    if (simpleNameExpression.getReferencedNameElementType() != JetTokens.FIELD_IDENTIFIER) {
356                        if (((PropertyDescriptor) variableDescriptor).getModality() != Modality.FINAL) {
357                            report(Errors.INITIALIZATION_USING_BACKING_FIELD_OPEN_SETTER.on(expression, variableDescriptor), ctxt);
358                        }
359                        else {
360                            report(Errors.INITIALIZATION_USING_BACKING_FIELD_CUSTOM_SETTER.on(expression, variableDescriptor), ctxt);
361                        }
362                        return true;
363                    }
364                }
365            }
366            return false;
367        }
368    
369        private boolean checkBackingField(@NotNull VariableContext cxtx, @NotNull JetElement element) {
370            VariableDescriptor variableDescriptor = cxtx.variableDescriptor;
371            boolean[] error = new boolean[1];
372            if (isCorrectBackingFieldReference((JetElement) element.getParent(), cxtx, error, false)) return false; // this expression has been already checked
373            if (!isCorrectBackingFieldReference(element, cxtx, error, true)) return false;
374            if (error[0]) return true;
375            if (!(variableDescriptor instanceof PropertyDescriptor)) {
376                report(Errors.NOT_PROPERTY_BACKING_FIELD.on(element), cxtx);
377                return true;
378            }
379            PsiElement property = BindingContextUtils.descriptorToDeclaration(trace.getBindingContext(), variableDescriptor);
380            boolean insideSelfAccessors = PsiTreeUtil.isAncestor(property, element, false);
381            if (!trace.get(BindingContext.BACKING_FIELD_REQUIRED, (PropertyDescriptor) variableDescriptor) &&
382                    !insideSelfAccessors) { // not to generate error in accessors of abstract properties, there is one: declared accessor of abstract property
383    
384                if (((PropertyDescriptor) variableDescriptor).getModality() == Modality.ABSTRACT) {
385                    report(NO_BACKING_FIELD_ABSTRACT_PROPERTY.on(element), cxtx);
386                }
387                else {
388                    report(NO_BACKING_FIELD_CUSTOM_ACCESSORS.on(element), cxtx);
389                }
390                return true;
391            }
392            if (insideSelfAccessors) return false;
393    
394            DeclarationDescriptor declarationDescriptor = BindingContextUtils.getEnclosingDescriptor(trace.getBindingContext(), element);
395    
396            DeclarationDescriptor containingDeclaration = variableDescriptor.getContainingDeclaration();
397            if ((containingDeclaration instanceof ClassDescriptor) && DescriptorUtils.isAncestor(containingDeclaration, declarationDescriptor, false)) {
398                return false;
399            }
400            report(Errors.INACCESSIBLE_BACKING_FIELD.on(element), cxtx);
401            return true;
402        }
403    
404        private boolean isCorrectBackingFieldReference(@Nullable JetElement element, VariableContext ctxt, boolean[] error, boolean reportError) {
405            error[0] = false;
406            if (JetPsiUtil.isBackingFieldReference(element)) {
407                return true;
408            }
409            if (element instanceof JetDotQualifiedExpression && isCorrectBackingFieldReference(
410                    ((JetDotQualifiedExpression) element).getSelectorExpression(), ctxt, error, false)) {
411                if (((JetDotQualifiedExpression) element).getReceiverExpression() instanceof JetThisExpression) {
412                    return true;
413                }
414                error[0] = true;
415                if (reportError) {
416                    report(Errors.INACCESSIBLE_BACKING_FIELD.on(element), ctxt);
417                }
418            }
419            return false;
420        }
421    
422        private void recordInitializedVariables(@NotNull Pseudocode pseudocode, @NotNull Map<Instruction, Edges<Map<VariableDescriptor,PseudocodeVariablesData.VariableInitState>>> initializersMap) {
423            Edges<Map<VariableDescriptor, VariableInitState>> initializers = initializersMap.get(pseudocode.getExitInstruction());
424            Set<VariableDescriptor> declaredVariables = pseudocodeVariablesData.getDeclaredVariables(pseudocode, false);
425            for (VariableDescriptor variable : declaredVariables) {
426                if (variable instanceof PropertyDescriptor) {
427                    PseudocodeVariablesData.VariableInitState variableInitState = initializers.in.get(variable);
428                    if (variableInitState == null) return;
429                    trace.record(BindingContext.IS_INITIALIZED, (PropertyDescriptor) variable, variableInitState.isInitialized);
430                }
431            }
432        }
433    
434    ////////////////////////////////////////////////////////////////////////////////
435    //  "Unused variable" & "unused value" analyses
436    
437        public void markUnusedVariables() {
438            Map<Instruction, Edges<Map<VariableDescriptor, VariableUseState>>> variableStatusData = pseudocodeVariablesData.getVariableUseStatusData();
439            final Map<Instruction, AbstractDiagnosticFactory> reportedDiagnosticMap = Maps.newHashMap();
440            InstructionDataAnalyzeStrategy<Map<VariableDescriptor, VariableUseState>> variableStatusAnalyzeStrategy =
441                    new InstructionDataAnalyzeStrategy<Map<VariableDescriptor, PseudocodeVariablesData.VariableUseState>>() {
442                @Override
443                public void execute(@NotNull Instruction instruction,
444                        @Nullable Map<VariableDescriptor, VariableUseState> in,
445                        @Nullable Map<VariableDescriptor, VariableUseState> out) {
446    
447                    assert in != null && out != null;
448                    VariableContext ctxt = new VariableUseContext(instruction, reportedDiagnosticMap, in, out);
449                    Set<VariableDescriptor> declaredVariables = pseudocodeVariablesData.getDeclaredVariables(instruction.getOwner(), false);
450                    VariableDescriptor variableDescriptor = PseudocodeUtil.extractVariableDescriptorIfAny(instruction, false,
451                                                                          trace.getBindingContext());
452                    if (variableDescriptor == null || !declaredVariables.contains(variableDescriptor) ||
453                        !DescriptorUtils.isLocal(variableDescriptor.getContainingDeclaration(), variableDescriptor)) return;
454                    PseudocodeVariablesData.VariableUseState variableUseState = in.get(variableDescriptor);
455                    if (instruction instanceof WriteValueInstruction) {
456                        if (trace.get(CAPTURED_IN_CLOSURE, variableDescriptor) != null) return;
457                        JetElement element = ((WriteValueInstruction) instruction).getElement();
458                        if (variableUseState != LAST_READ) {
459                            if (element instanceof JetBinaryExpression &&
460                                ((JetBinaryExpression) element).getOperationToken() == JetTokens.EQ) {
461                                JetExpression right = ((JetBinaryExpression) element).getRight();
462                                if (right != null) {
463                                    report(Errors.UNUSED_VALUE.on(right, right, variableDescriptor), ctxt);
464                                }
465                            }
466                            else if (element instanceof JetPostfixExpression) {
467                                IElementType operationToken = ((JetPostfixExpression) element).getOperationReference().getReferencedNameElementType();
468                                if (operationToken == JetTokens.PLUSPLUS || operationToken == JetTokens.MINUSMINUS) {
469                                    report(Errors.UNUSED_CHANGED_VALUE.on(element, element), ctxt);
470                                }
471                            }
472                        }
473                    }
474                    else if (instruction instanceof VariableDeclarationInstruction) {
475                        JetDeclaration element = ((VariableDeclarationInstruction) instruction).getVariableDeclarationElement();
476                        if (!(element instanceof JetNamedDeclaration)) return;
477                        PsiElement nameIdentifier = ((JetNamedDeclaration) element).getNameIdentifier();
478                        if (nameIdentifier == null) return;
479                        if (!VariableUseState.isUsed(variableUseState)) {
480                            if (JetPsiUtil.isVariableNotParameterDeclaration(element)) {
481                                report(Errors.UNUSED_VARIABLE.on((JetNamedDeclaration) element, variableDescriptor), ctxt);
482                            }
483                            else if (element instanceof JetParameter) {
484                                PsiElement psiElement = element.getParent().getParent();
485                                if (psiElement instanceof JetFunction) {
486                                    boolean isMain = (psiElement instanceof JetNamedFunction) && JetMainDetector.isMain((JetNamedFunction) psiElement);
487                                    if (psiElement instanceof JetFunctionLiteral) return;
488                                    DeclarationDescriptor descriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, psiElement);
489                                    assert descriptor instanceof FunctionDescriptor : psiElement.getText();
490                                    FunctionDescriptor functionDescriptor = (FunctionDescriptor) descriptor;
491                                    if (!isMain && !functionDescriptor.getModality().isOverridable() && functionDescriptor.getOverriddenDescriptors().isEmpty()) {
492                                        report(Errors.UNUSED_PARAMETER.on((JetParameter) element, variableDescriptor), ctxt);
493                                    }
494                                }
495                            }
496                        }
497                        else if (variableUseState == ONLY_WRITTEN_NEVER_READ && JetPsiUtil.isVariableNotParameterDeclaration(element)) {
498                            report(Errors.ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE.on((JetNamedDeclaration) element, variableDescriptor), ctxt);
499                        }
500                        else if (variableUseState == LAST_WRITTEN && element instanceof JetVariableDeclaration) {
501                            if (element instanceof JetProperty) {
502                                JetExpression initializer = ((JetProperty) element).getInitializer();
503                                if (initializer != null) {
504                                    report(Errors.VARIABLE_WITH_REDUNDANT_INITIALIZER.on(initializer, variableDescriptor), ctxt);
505                                }
506                            }
507                            else if (element instanceof JetMultiDeclarationEntry) {
508                                report(VARIABLE_WITH_REDUNDANT_INITIALIZER.on(element, variableDescriptor), ctxt);
509                            }
510                        }
511                    }
512                }
513            };
514            PseudocodeTraverser.traverse(pseudocode, BACKWARD, variableStatusData, variableStatusAnalyzeStrategy);
515        }
516    
517    ////////////////////////////////////////////////////////////////////////////////
518    //  "Unused literals" in block
519    
520        public void markUnusedLiteralsInBlock() {
521            assert pseudocode != null;
522            final Map<Instruction, AbstractDiagnosticFactory> reportedDiagnosticMap = Maps.newHashMap();
523            PseudocodeTraverser.traverse(
524                    pseudocode, FORWARD, new InstructionAnalyzeStrategy() {
525                @Override
526                public void execute(@NotNull Instruction instruction) {
527                    if (!(instruction instanceof ReadValueInstruction)) return;
528                    VariableContext ctxt = new VariableContext(instruction, reportedDiagnosticMap);
529                    JetElement element =
530                            ((ReadValueInstruction) instruction).getElement();
531                    if (!(element instanceof JetFunctionLiteralExpression
532                          || element instanceof JetConstantExpression
533                          || element instanceof JetStringTemplateExpression
534                          || element instanceof JetSimpleNameExpression)) {
535                        return;
536                    }
537                    PsiElement parent = element.getParent();
538                    if (parent instanceof JetBlockExpression) {
539                        if (!JetPsiUtil.isImplicitlyUsed(element)) {
540                            if (element instanceof JetFunctionLiteralExpression) {
541                                report(Errors.UNUSED_FUNCTION_LITERAL.on((JetFunctionLiteralExpression) element), ctxt);
542                            }
543                            else {
544                                report(Errors.UNUSED_EXPRESSION.on(element), ctxt);
545                            }
546                        }
547                    }
548                }
549            });
550        }
551    
552    ////////////////////////////////////////////////////////////////////////////////
553    // Utility classes and methods
554    
555        /**
556         * The method provides reporting of the same diagnostic only once for copied instructions
557         * (depends on whether it should be reported for all or only for one of the copies)
558         */
559        private void report(
560                @NotNull Diagnostic diagnostic,
561                @NotNull VariableContext ctxt
562        ) {
563            Instruction instruction = ctxt.instruction;
564            if (instruction.getCopies().isEmpty()) {
565                trace.report(diagnostic);
566                return;
567            }
568            Map<Instruction, AbstractDiagnosticFactory> previouslyReported = ctxt.reportedDiagnosticMap;
569            previouslyReported.put(instruction, diagnostic.getFactory());
570    
571            boolean alreadyReported = false;
572            boolean sameErrorForAllCopies = true;
573            for (Instruction copy : instruction.getCopies()) {
574                AbstractDiagnosticFactory previouslyReportedErrorFactory = previouslyReported.get(copy);
575                if (previouslyReportedErrorFactory != null) {
576                    alreadyReported = true;
577                }
578    
579                if (previouslyReportedErrorFactory != diagnostic.getFactory()) {
580                    sameErrorForAllCopies = false;
581                }
582            }
583    
584            if (mustBeReportedOnAllCopies(diagnostic.getFactory())) {
585                if (sameErrorForAllCopies) {
586                    trace.report(diagnostic);
587                }
588            }
589            else {
590                //only one reporting required
591                if (!alreadyReported) {
592                    trace.report(diagnostic);
593                }
594            }
595        }
596    
597        private static boolean mustBeReportedOnAllCopies(@NotNull AbstractDiagnosticFactory diagnosticFactory) {
598            return diagnosticFactory == UNUSED_VARIABLE
599                   || diagnosticFactory == UNUSED_PARAMETER
600                   || diagnosticFactory == UNUSED_CHANGED_VALUE;
601        }
602    
603    
604    
605        private class VariableContext {
606            final Map<Instruction, AbstractDiagnosticFactory> reportedDiagnosticMap;
607            final Instruction instruction;
608            final VariableDescriptor variableDescriptor;
609    
610            private VariableContext(
611                    @NotNull Instruction instruction,
612                    @NotNull Map<Instruction, AbstractDiagnosticFactory> map
613            ) {
614                this.instruction = instruction;
615                reportedDiagnosticMap = map;
616                variableDescriptor = PseudocodeUtil.extractVariableDescriptorIfAny(instruction, true, trace.getBindingContext());
617            }
618        }
619    
620        private class VariableInitContext extends VariableContext {
621            final VariableInitState enterInitState;
622            final VariableInitState exitInitState;
623    
624            private VariableInitContext(
625                    @NotNull Instruction instruction,
626                    @NotNull Map<Instruction, AbstractDiagnosticFactory> map,
627                    @NotNull Map<VariableDescriptor, VariableInitState> in,
628                    @NotNull Map<VariableDescriptor, VariableInitState> out
629            ) {
630                super(instruction, map);
631                enterInitState = variableDescriptor != null ? in.get(variableDescriptor) : null;
632                exitInitState = variableDescriptor != null ? out.get(variableDescriptor) : null;
633            }
634        }
635    
636        private class VariableUseContext extends VariableContext {
637            final VariableUseState enterUseState;
638            final VariableUseState exitUseState;
639    
640    
641            private VariableUseContext(
642                    @NotNull Instruction instruction,
643                    @NotNull Map<Instruction, AbstractDiagnosticFactory> map,
644                    @NotNull Map<VariableDescriptor, VariableUseState> in,
645                    @NotNull Map<VariableDescriptor, VariableUseState> out
646            ) {
647                super(instruction, map);
648                enterUseState = variableDescriptor != null ? in.get(variableDescriptor) : null;
649                exitUseState = variableDescriptor != null ? out.get(variableDescriptor) : null;
650            }
651        }
652    }