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