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