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