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