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