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