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