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