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 String functionName = functionDescriptor.getName().asString(); 653 if (isMain 654 || functionDescriptor.getModality().isOverridable() 655 || !functionDescriptor.getOverriddenDescriptors().isEmpty() 656 || "get".equals(functionName) || "set".equals(functionName) || "propertyDelegated".equals(functionName) 657 ) { 658 return; 659 } 660 report(Errors.UNUSED_PARAMETER.on((JetParameter) element, variableDescriptor), ctxt); 661 } else if (owner instanceof JetClass) { 662 if (!((JetParameter) element).hasValOrVarNode() && !((JetClass) owner).isAnnotation()) { 663 report(Errors.UNUSED_PARAMETER.on((JetParameter) element, variableDescriptor), ctxt); 664 } 665 } 666 } 667 } 668 else if (variableUseState == ONLY_WRITTEN_NEVER_READ && JetPsiUtil.isVariableNotParameterDeclaration(element)) { 669 report(Errors.ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE.on((JetNamedDeclaration) element, variableDescriptor), ctxt); 670 } 671 else if (variableUseState == WRITTEN_AFTER_READ && element instanceof JetVariableDeclaration) { 672 if (element instanceof JetProperty) { 673 JetExpression initializer = ((JetProperty) element).getInitializer(); 674 if (initializer != null) { 675 report(Errors.VARIABLE_WITH_REDUNDANT_INITIALIZER.on(initializer, variableDescriptor), ctxt); 676 } 677 } 678 else if (element instanceof JetMultiDeclarationEntry) { 679 report(VARIABLE_WITH_REDUNDANT_INITIALIZER.on(element, variableDescriptor), ctxt); 680 } 681 } 682 } 683 } 684 }; 685 PseudocodeTraverserPackage.traverse(pseudocode, TraversalOrder.BACKWARD, variableStatusData, variableStatusAnalyzeStrategy); 686 } 687 688 //////////////////////////////////////////////////////////////////////////////// 689 // "Unused expressions" in block 690 691 public void markUnusedExpressions() { 692 final Map<Instruction, DiagnosticFactory<?>> reportedDiagnosticMap = Maps.newHashMap(); 693 PseudocodeTraverserPackage.traverse( 694 pseudocode, FORWARD, new JetFlowInformationProvider.FunctionVoid1<Instruction>() { 695 @Override 696 public void execute(@NotNull Instruction instruction) { 697 if (!(instruction instanceof JetElementInstruction)) return; 698 699 JetElement element = ((JetElementInstruction)instruction).getElement(); 700 if (!(element instanceof JetExpression)) return; 701 702 if (BindingContextUtilPackage.isUsedAsStatement((JetExpression) element, trace.getBindingContext()) 703 && PseudocodePackage.getSideEffectFree(instruction)) { 704 VariableContext ctxt = new VariableContext(instruction, reportedDiagnosticMap); 705 report( 706 element instanceof JetFunctionLiteralExpression 707 ? Errors.UNUSED_FUNCTION_LITERAL.on((JetFunctionLiteralExpression) element) 708 : Errors.UNUSED_EXPRESSION.on(element), 709 ctxt 710 ); 711 } 712 } 713 } 714 ); 715 } 716 717 //////////////////////////////////////////////////////////////////////////////// 718 // Statements 719 720 public void markStatements() { 721 PseudocodeTraverserPackage.traverse( 722 pseudocode, FORWARD, new JetFlowInformationProvider.FunctionVoid1<Instruction>() { 723 @Override 724 public void execute(@NotNull Instruction instruction) { 725 PseudoValue value = instruction instanceof InstructionWithValue 726 ? ((InstructionWithValue) instruction).getOutputValue() 727 : null; 728 Pseudocode pseudocode = instruction.getOwner(); 729 boolean isUsedAsExpression = !pseudocode.getUsages(value).isEmpty(); 730 for (JetElement element : pseudocode.getValueElements(value)) { 731 trace.record(BindingContext.USED_AS_EXPRESSION, element, isUsedAsExpression); 732 } 733 } 734 } 735 ); 736 } 737 738 public void markWhenWithoutElse() { 739 PseudocodeTraverserPackage.traverse( 740 pseudocode, FORWARD, new JetFlowInformationProvider.FunctionVoid1<Instruction>() { 741 @Override 742 public void execute(@NotNull Instruction instruction) { 743 PseudoValue value = instruction instanceof InstructionWithValue 744 ? ((InstructionWithValue) instruction).getOutputValue() 745 : null; 746 for (JetElement element : instruction.getOwner().getValueElements(value)) { 747 if (element instanceof JetWhenExpression) { 748 JetWhenExpression whenExpression = (JetWhenExpression) element; 749 if (whenExpression.getElseExpression() == null && WhenChecker.mustHaveElse(whenExpression, trace)) { 750 trace.report(NO_ELSE_IN_WHEN.on(whenExpression)); 751 } 752 } 753 } 754 } 755 } 756 ); 757 } 758 759 //////////////////////////////////////////////////////////////////////////////// 760 // Tail calls 761 762 public void markTailCalls() { 763 final DeclarationDescriptor subroutineDescriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, subroutine); 764 if (!(subroutineDescriptor instanceof FunctionDescriptor)) return; 765 if (!KotlinBuiltIns.isTailRecursive(subroutineDescriptor)) return; 766 767 // finally blocks are copied which leads to multiple diagnostics reported on one instruction 768 class KindAndCall { 769 TailRecursionKind kind; 770 ResolvedCall<?> call; 771 772 KindAndCall(TailRecursionKind kind, ResolvedCall<?> call) { 773 this.kind = kind; 774 this.call = call; 775 } 776 } 777 final Map<JetElement, KindAndCall> calls = new HashMap<JetElement, KindAndCall>(); 778 PseudocodeTraverserPackage.traverse( 779 pseudocode, 780 FORWARD, 781 new FunctionVoid1<Instruction>() { 782 public void execute(@NotNull Instruction instruction) { 783 if (!(instruction instanceof CallInstruction)) return; 784 CallInstruction callInstruction = (CallInstruction) instruction; 785 786 ResolvedCall<?> resolvedCall = getResolvedCall(callInstruction.getElement(), trace.getBindingContext()); 787 if (resolvedCall == null) return; 788 789 // is this a recursive call? 790 CallableDescriptor functionDescriptor = resolvedCall.getResultingDescriptor(); 791 if (!functionDescriptor.getOriginal().equals(subroutineDescriptor)) return; 792 793 JetElement element = callInstruction.getElement(); 794 //noinspection unchecked 795 JetExpression parent = PsiTreeUtil.getParentOfType( 796 element, 797 JetTryExpression.class, JetFunction.class, JetClassInitializer.class 798 ); 799 800 if (parent instanceof JetTryExpression) { 801 // We do not support tail calls Collections.singletonMap() try-catch-finally, for simplicity of the mental model 802 // very few cases there would be real tail-calls, and it's often not so easy for the user to see why 803 calls.put(element, new KindAndCall(IN_TRY, resolvedCall)); 804 return; 805 } 806 807 boolean isTail = PseudocodeTraverserPackage.traverseFollowingInstructions( 808 callInstruction, 809 new HashSet<Instruction>(), 810 FORWARD, 811 new TailRecursionDetector(subroutine, callInstruction) 812 ); 813 814 boolean sameDispatchReceiver = sameDispatchReceiver(resolvedCall); 815 816 TailRecursionKind kind = isTail && sameDispatchReceiver ? TAIL_CALL : NON_TAIL; 817 818 KindAndCall kindAndCall = calls.get(element); 819 calls.put(element, 820 new KindAndCall( 821 combineKinds(kind, kindAndCall == null ? null : kindAndCall.kind), 822 resolvedCall 823 ) 824 ); 825 } 826 } 827 ); 828 boolean hasTailCalls = false; 829 for (Map.Entry<JetElement, KindAndCall> entry : calls.entrySet()) { 830 JetElement element = entry.getKey(); 831 KindAndCall kindAndCall = entry.getValue(); 832 switch (kindAndCall.kind) { 833 case TAIL_CALL: 834 trace.record(TAIL_RECURSION_CALL, kindAndCall.call, TailRecursionKind.TAIL_CALL); 835 hasTailCalls = true; 836 break; 837 case IN_TRY: 838 trace.report(Errors.TAIL_RECURSION_IN_TRY_IS_NOT_SUPPORTED.on(element)); 839 break; 840 case NON_TAIL: 841 trace.report(Errors.NON_TAIL_RECURSIVE_CALL.on(element)); 842 break; 843 } 844 } 845 846 if (!hasTailCalls) { 847 trace.report(Errors.NO_TAIL_CALLS_FOUND.on((JetNamedFunction) subroutine)); 848 } 849 } 850 851 private boolean sameDispatchReceiver(ResolvedCall<?> resolvedCall) { 852 // A tail call is not allowed to change dispatch receiver 853 // class C { 854 // fun foo(other: C) { 855 // other.foo(this) // not a tail call 856 // } 857 // } 858 ReceiverParameterDescriptor dispatchReceiverParameter = resolvedCall.getResultingDescriptor().getDispatchReceiverParameter(); 859 ReceiverValue dispatchReceiverValue = resolvedCall.getDispatchReceiver(); 860 if (dispatchReceiverParameter == null || !dispatchReceiverValue.exists()) return true; 861 862 DeclarationDescriptor classDescriptor = null; 863 if (dispatchReceiverValue instanceof ThisReceiver) { 864 // foo() -- implicit receiver 865 classDescriptor = ((ThisReceiver) dispatchReceiverValue).getDeclarationDescriptor(); 866 } 867 else if (dispatchReceiverValue instanceof ExpressionReceiver) { 868 JetExpression expression = JetPsiUtil.deparenthesize(((ExpressionReceiver) dispatchReceiverValue).getExpression()); 869 if (expression instanceof JetThisExpression) { 870 // this.foo() -- explicit receiver 871 JetThisExpression thisExpression = (JetThisExpression) expression; 872 classDescriptor = trace.get(BindingContext.REFERENCE_TARGET, thisExpression.getInstanceReference()); 873 } 874 } 875 return dispatchReceiverParameter.getContainingDeclaration() == classDescriptor; 876 } 877 878 private static TailRecursionKind combineKinds(TailRecursionKind kind, @Nullable TailRecursionKind existingKind) { 879 TailRecursionKind resultingKind; 880 if (existingKind == null || existingKind == kind) { 881 resultingKind = kind; 882 } 883 else { 884 if (check(kind, existingKind, IN_TRY, TAIL_CALL)) { 885 resultingKind = IN_TRY; 886 } 887 else if (check(kind, existingKind, IN_TRY, NON_TAIL)) { 888 resultingKind = IN_TRY; 889 } 890 else { 891 // TAIL_CALL, NON_TAIL 892 resultingKind = NON_TAIL; 893 } 894 } 895 return resultingKind; 896 } 897 898 private static boolean check(Object a, Object b, Object x, Object y) { 899 return (a == x && b == y) || (a == y && b == x); 900 } 901 902 //////////////////////////////////////////////////////////////////////////////// 903 // Utility classes and methods 904 905 /** 906 * The method provides reporting of the same diagnostic only once for copied instructions 907 * (depends on whether it should be reported for all or only for one of the copies) 908 */ 909 private void report( 910 @NotNull Diagnostic diagnostic, 911 @NotNull VariableContext ctxt 912 ) { 913 Instruction instruction = ctxt.instruction; 914 if (instruction.getCopies().isEmpty()) { 915 trace.report(diagnostic); 916 return; 917 } 918 Map<Instruction, DiagnosticFactory<?>> previouslyReported = ctxt.reportedDiagnosticMap; 919 previouslyReported.put(instruction, diagnostic.getFactory()); 920 921 boolean alreadyReported = false; 922 boolean sameErrorForAllCopies = true; 923 for (Instruction copy : instruction.getCopies()) { 924 DiagnosticFactory<?> previouslyReportedErrorFactory = previouslyReported.get(copy); 925 if (previouslyReportedErrorFactory != null) { 926 alreadyReported = true; 927 } 928 929 if (previouslyReportedErrorFactory != diagnostic.getFactory()) { 930 sameErrorForAllCopies = false; 931 } 932 } 933 934 if (mustBeReportedOnAllCopies(diagnostic.getFactory())) { 935 if (sameErrorForAllCopies) { 936 trace.report(diagnostic); 937 } 938 } 939 else { 940 //only one reporting required 941 if (!alreadyReported) { 942 trace.report(diagnostic); 943 } 944 } 945 } 946 947 private static boolean mustBeReportedOnAllCopies(@NotNull DiagnosticFactory<?> diagnosticFactory) { 948 return diagnosticFactory == UNUSED_VARIABLE 949 || diagnosticFactory == UNUSED_PARAMETER 950 || diagnosticFactory == UNUSED_CHANGED_VALUE; 951 } 952 953 954 private class VariableContext { 955 final Map<Instruction, DiagnosticFactory<?>> reportedDiagnosticMap; 956 final Instruction instruction; 957 final VariableDescriptor variableDescriptor; 958 959 private VariableContext( 960 @NotNull Instruction instruction, 961 @NotNull Map<Instruction, DiagnosticFactory<?>> map 962 ) { 963 this.instruction = instruction; 964 reportedDiagnosticMap = map; 965 variableDescriptor = PseudocodeUtil.extractVariableDescriptorIfAny(instruction, true, trace.getBindingContext()); 966 } 967 } 968 969 private class VariableInitContext extends VariableContext { 970 final VariableInitState enterInitState; 971 final VariableInitState exitInitState; 972 973 private VariableInitContext( 974 @NotNull Instruction instruction, 975 @NotNull Map<Instruction, DiagnosticFactory<?>> map, 976 @NotNull Map<VariableDescriptor, VariableInitState> in, 977 @NotNull Map<VariableDescriptor, VariableInitState> out, 978 @NotNull LexicalScopeVariableInfo lexicalScopeVariableInfo 979 ) { 980 super(instruction, map); 981 enterInitState = initialize(variableDescriptor, lexicalScopeVariableInfo, in); 982 exitInitState = initialize(variableDescriptor, lexicalScopeVariableInfo, out); 983 } 984 985 private VariableInitState initialize( 986 VariableDescriptor variableDescriptor, 987 LexicalScopeVariableInfo lexicalScopeVariableInfo, 988 Map<VariableDescriptor, VariableInitState> map 989 ) { 990 if (variableDescriptor == null) return null; 991 VariableInitState state = map.get(variableDescriptor); 992 if (state != null) return state; 993 return PseudocodeVariablesData.getDefaultValueForInitializers(variableDescriptor, instruction, lexicalScopeVariableInfo); 994 } 995 } 996 997 private class VariableUseContext extends VariableContext { 998 final VariableUseState enterUseState; 999 final VariableUseState exitUseState; 1000 1001 1002 private VariableUseContext( 1003 @NotNull Instruction instruction, 1004 @NotNull Map<Instruction, DiagnosticFactory<?>> map, 1005 @NotNull Map<VariableDescriptor, VariableUseState> in, 1006 @NotNull Map<VariableDescriptor, VariableUseState> out 1007 ) { 1008 super(instruction, map); 1009 enterUseState = variableDescriptor != null ? in.get(variableDescriptor) : null; 1010 exitUseState = variableDescriptor != null ? out.get(variableDescriptor) : null; 1011 } 1012 } 1013 1014 //TODO after KT-4621 rewrite to Kotlin 1015 public abstract static class InstructionDataAnalyzeStrategy<D> implements Function3<Instruction, D, D, Unit> { 1016 @Override 1017 public Unit invoke(Instruction instruction, D enterData, D exitData) { 1018 execute(instruction, enterData, exitData); 1019 return Unit.INSTANCE$; 1020 } 1021 1022 public abstract void execute(Instruction instruction, D enterData, D exitData); 1023 } 1024 1025 public abstract static class FunctionVoid1<P> implements Function1<P, Unit> { 1026 @Override 1027 public Unit invoke(P p) { 1028 execute(p); 1029 return Unit.INSTANCE$; 1030 } 1031 1032 public abstract void execute(P p); 1033 } 1034 }