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