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
017package org.jetbrains.jet.lang.cfg;
018
019import com.google.common.collect.Lists;
020import com.google.common.collect.Maps;
021import com.google.common.collect.Sets;
022import com.intellij.psi.PsiElement;
023import com.intellij.psi.tree.IElementType;
024import com.intellij.psi.util.PsiTreeUtil;
025import org.jetbrains.annotations.NotNull;
026import org.jetbrains.annotations.Nullable;
027import org.jetbrains.jet.lang.cfg.pseudocode.*;
028import org.jetbrains.jet.lang.cfg.PseudocodeTraverser.*;
029import org.jetbrains.jet.lang.cfg.PseudocodeVariablesData.VariableInitState;
030import org.jetbrains.jet.lang.cfg.PseudocodeVariablesData.VariableUseState;
031import org.jetbrains.jet.lang.descriptors.*;
032import org.jetbrains.jet.lang.diagnostics.AbstractDiagnosticFactory;
033import org.jetbrains.jet.lang.diagnostics.Diagnostic;
034import org.jetbrains.jet.lang.diagnostics.Errors;
035import org.jetbrains.jet.lang.psi.*;
036import org.jetbrains.jet.lang.resolve.BindingContext;
037import org.jetbrains.jet.lang.resolve.BindingContextUtils;
038import org.jetbrains.jet.lang.resolve.BindingTrace;
039import org.jetbrains.jet.lang.resolve.DescriptorUtils;
040import org.jetbrains.jet.lang.types.JetType;
041import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
042import org.jetbrains.jet.lexer.JetTokens;
043import org.jetbrains.jet.plugin.JetMainDetector;
044
045import java.util.*;
046
047import static org.jetbrains.jet.lang.cfg.PseudocodeTraverser.TraversalOrder.BACKWARD;
048import static org.jetbrains.jet.lang.cfg.PseudocodeTraverser.TraversalOrder.FORWARD;
049import static org.jetbrains.jet.lang.cfg.PseudocodeVariablesData.VariableUseState.*;
050import static org.jetbrains.jet.lang.diagnostics.Errors.*;
051import static org.jetbrains.jet.lang.resolve.BindingContext.CAPTURED_IN_CLOSURE;
052import static org.jetbrains.jet.lang.types.TypeUtils.NO_EXPECTED_TYPE;
053
054public 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}