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