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