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 017package org.jetbrains.jet.lang.cfg; 018 019import com.google.common.collect.Lists; 020import com.google.common.collect.Maps; 021import com.google.common.collect.Sets; 022import com.intellij.psi.PsiElement; 023import com.intellij.psi.tree.IElementType; 024import com.intellij.psi.util.PsiTreeUtil; 025import org.jetbrains.annotations.NotNull; 026import org.jetbrains.annotations.Nullable; 027import org.jetbrains.jet.lang.cfg.pseudocode.*; 028import org.jetbrains.jet.lang.cfg.PseudocodeTraverser.*; 029import org.jetbrains.jet.lang.cfg.PseudocodeVariablesData.VariableInitState; 030import org.jetbrains.jet.lang.cfg.PseudocodeVariablesData.VariableUseState; 031import org.jetbrains.jet.lang.descriptors.*; 032import org.jetbrains.jet.lang.diagnostics.AbstractDiagnosticFactory; 033import org.jetbrains.jet.lang.diagnostics.Diagnostic; 034import org.jetbrains.jet.lang.diagnostics.Errors; 035import org.jetbrains.jet.lang.psi.*; 036import org.jetbrains.jet.lang.resolve.BindingContext; 037import org.jetbrains.jet.lang.resolve.BindingContextUtils; 038import org.jetbrains.jet.lang.resolve.BindingTrace; 039import org.jetbrains.jet.lang.resolve.DescriptorUtils; 040import org.jetbrains.jet.lang.types.JetType; 041import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; 042import org.jetbrains.jet.lexer.JetTokens; 043import org.jetbrains.jet.plugin.JetMainDetector; 044 045import java.util.*; 046 047import static org.jetbrains.jet.lang.cfg.PseudocodeTraverser.TraversalOrder.BACKWARD; 048import static org.jetbrains.jet.lang.cfg.PseudocodeTraverser.TraversalOrder.FORWARD; 049import static org.jetbrains.jet.lang.cfg.PseudocodeVariablesData.VariableUseState.*; 050import static org.jetbrains.jet.lang.diagnostics.Errors.*; 051import static org.jetbrains.jet.lang.resolve.BindingContext.CAPTURED_IN_CLOSURE; 052import static org.jetbrains.jet.lang.types.TypeUtils.NO_EXPECTED_TYPE; 053 054public class JetFlowInformationProvider { 055 056 private final JetElement subroutine; 057 private final Pseudocode pseudocode; 058 private final PseudocodeVariablesData pseudocodeVariablesData; 059 private BindingTrace trace; 060 061 public JetFlowInformationProvider( 062 @NotNull JetElement declaration, 063 @NotNull BindingTrace trace) { 064 065 subroutine = declaration; 066 this.trace = trace; 067 pseudocode = new JetControlFlowProcessor(trace).generatePseudocode(declaration); 068 pseudocodeVariablesData = new PseudocodeVariablesData(pseudocode, trace.getBindingContext()); 069 } 070 071 private void collectReturnExpressions(@NotNull final Collection<JetElement> returnedExpressions) { 072 final Set<Instruction> instructions = Sets.newHashSet(pseudocode.getInstructions()); 073 SubroutineExitInstruction exitInstruction = pseudocode.getExitInstruction(); 074 for (Instruction previousInstruction : exitInstruction.getPreviousInstructions()) { 075 previousInstruction.accept(new InstructionVisitor() { 076 @Override 077 public void visitReturnValue(ReturnValueInstruction instruction) { 078 if (instructions.contains(instruction)) { //exclude non-local return expressions 079 returnedExpressions.add(instruction.getElement()); 080 } 081 } 082 083 @Override 084 public void visitReturnNoValue(ReturnNoValueInstruction instruction) { 085 if (instructions.contains(instruction)) { 086 returnedExpressions.add(instruction.getElement()); 087 } 088 } 089 090 091 @Override 092 public void visitJump(AbstractJumpInstruction instruction) { 093 // Nothing 094 } 095 096 @Override 097 public void visitUnconditionalJump(UnconditionalJumpInstruction instruction) { 098 redirectToPrevInstructions(instruction); 099 } 100 101 private void redirectToPrevInstructions(Instruction instruction) { 102 for (Instruction previousInstruction : instruction.getPreviousInstructions()) { 103 previousInstruction.accept(this); 104 } 105 } 106 107 @Override 108 public void visitNondeterministicJump(NondeterministicJumpInstruction instruction) { 109 redirectToPrevInstructions(instruction); 110 } 111 112 @Override 113 public void visitInstruction(Instruction instruction) { 114 if (instruction instanceof JetElementInstruction) { 115 JetElementInstruction elementInstruction = (JetElementInstruction) instruction; 116 returnedExpressions.add(elementInstruction.getElement()); 117 } 118 else { 119 throw new IllegalStateException(instruction + " precedes the exit point"); 120 } 121 } 122 }); 123 } 124 } 125 126 public void checkDefiniteReturn(final @NotNull JetType expectedReturnType) { 127 assert subroutine instanceof JetDeclarationWithBody; 128 JetDeclarationWithBody function = (JetDeclarationWithBody) subroutine; 129 130 JetExpression bodyExpression = function.getBodyExpression(); 131 if (bodyExpression == null) return; 132 133 List<JetElement> returnedExpressions = Lists.newArrayList(); 134 collectReturnExpressions(returnedExpressions); 135 136 final boolean blockBody = function.hasBlockBody(); 137 138 final Set<JetElement> rootUnreachableElements = collectUnreachableCode(); 139 for (JetElement element : rootUnreachableElements) { 140 trace.report(UNREACHABLE_CODE.on(element)); 141 } 142 143 final boolean[] noReturnError = new boolean[] { false }; 144 for (JetElement returnedExpression : returnedExpressions) { 145 returnedExpression.accept(new JetVisitorVoid() { 146 @Override 147 public void visitReturnExpression(JetReturnExpression expression) { 148 if (!blockBody) { 149 trace.report(RETURN_IN_FUNCTION_WITH_EXPRESSION_BODY.on(expression)); 150 } 151 } 152 153 @Override 154 public void visitExpression(JetExpression expression) { 155 if (blockBody && expectedReturnType != NO_EXPECTED_TYPE && !KotlinBuiltIns.getInstance().isUnit(expectedReturnType) && !rootUnreachableElements.contains(expression)) { 156 noReturnError[0] = true; 157 } 158 } 159 }); 160 } 161 if (noReturnError[0]) { 162 trace.report(NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY.on(function)); 163 } 164 } 165 166 private Set<JetElement> collectUnreachableCode() { 167 Collection<JetElement> unreachableElements = Lists.newArrayList(); 168 for (Instruction deadInstruction : pseudocode.getDeadInstructions()) { 169 if (deadInstruction instanceof JetElementInstruction && 170 !(deadInstruction instanceof ReadUnitValueInstruction)) { 171 unreachableElements.add(((JetElementInstruction) deadInstruction).getElement()); 172 } 173 } 174 // This is needed in order to highlight only '1 < 2' and not '1', '<' and '2' as well 175 return JetPsiUtil.findRootExpressions(unreachableElements); 176 } 177 178//////////////////////////////////////////////////////////////////////////////// 179// Uninitialized variables analysis 180 181 public void markUninitializedVariables() { 182 final Collection<VariableDescriptor> varWithUninitializedErrorGenerated = Sets.newHashSet(); 183 final Collection<VariableDescriptor> varWithValReassignErrorGenerated = Sets.newHashSet(); 184 final boolean processClassOrObject = subroutine instanceof JetClassOrObject; 185 186 Map<Instruction, Edges<Map<VariableDescriptor,VariableInitState>>> initializers = pseudocodeVariablesData.getVariableInitializers(); 187 final Set<VariableDescriptor> declaredVariables = pseudocodeVariablesData.getDeclaredVariables(pseudocode, true); 188 189 final Map<Instruction, AbstractDiagnosticFactory> reportedDiagnosticMap = Maps.newHashMap(); 190 191 PseudocodeTraverser.traverse(pseudocode, FORWARD, initializers, new InstructionDataAnalyzeStrategy<Map<VariableDescriptor, PseudocodeVariablesData.VariableInitState>>() { 192 @Override 193 public void execute(@NotNull Instruction instruction, 194 @Nullable Map<VariableDescriptor, VariableInitState> in, 195 @Nullable Map<VariableDescriptor, VariableInitState> out) { 196 assert in != null && out != null; 197 VariableInitContext ctxt = new VariableInitContext(instruction, reportedDiagnosticMap, in, out); 198 if (ctxt.variableDescriptor == null) return; 199 if (instruction instanceof ReadValueInstruction) { 200 JetElement element = ((ReadValueInstruction) instruction).getElement(); 201 boolean error = checkBackingField(ctxt, element); 202 if (!error && declaredVariables.contains(ctxt.variableDescriptor)) { 203 checkIsInitialized(ctxt, element, varWithUninitializedErrorGenerated); 204 } 205 return; 206 } 207 if (!(instruction instanceof WriteValueInstruction)) return; 208 JetElement element = ((WriteValueInstruction) instruction).getlValue(); 209 boolean error = checkBackingField(ctxt, element); 210 if (!(element instanceof JetExpression)) return; 211 if (!error) { 212 error = checkValReassignment(ctxt, (JetExpression) element, varWithValReassignErrorGenerated); 213 } 214 if (!error && processClassOrObject) { 215 error = checkAssignmentBeforeDeclaration(ctxt, (JetExpression) element); 216 } 217 if (!error && processClassOrObject) { 218 checkInitializationUsingBackingField(ctxt, (JetExpression) element); 219 } 220 } 221 }); 222 } 223 224 public void recordInitializedVariables() { 225 Pseudocode pseudocode = pseudocodeVariablesData.getPseudocode(); 226 Map<Instruction, Edges<Map<VariableDescriptor,VariableInitState>>> initializers = pseudocodeVariablesData.getVariableInitializers(); 227 recordInitializedVariables(pseudocode, initializers); 228 for (LocalDeclarationInstruction instruction : pseudocode.getLocalDeclarations()) { 229 recordInitializedVariables(instruction.getBody(), initializers); 230 } 231 } 232 233 private void checkIsInitialized( 234 @NotNull VariableInitContext ctxt, 235 @NotNull JetElement element, 236 @NotNull Collection<VariableDescriptor> varWithUninitializedErrorGenerated 237 ) { 238 if (!(element instanceof JetSimpleNameExpression)) return; 239 240 boolean isInitialized = ctxt.exitInitState.isInitialized; 241 VariableDescriptor variableDescriptor = ctxt.variableDescriptor; 242 if (variableDescriptor instanceof PropertyDescriptor) { 243 if (!trace.get(BindingContext.BACKING_FIELD_REQUIRED, (PropertyDescriptor) variableDescriptor)) { 244 isInitialized = true; 245 } 246 } 247 if (!isInitialized && !varWithUninitializedErrorGenerated.contains(variableDescriptor)) { 248 if (!(variableDescriptor instanceof PropertyDescriptor)) { 249 varWithUninitializedErrorGenerated.add(variableDescriptor); 250 } 251 if (variableDescriptor instanceof ValueParameterDescriptor) { 252 report(Errors.UNINITIALIZED_PARAMETER.on((JetSimpleNameExpression) element, 253 (ValueParameterDescriptor) variableDescriptor), ctxt); 254 } 255 else { 256 report(Errors.UNINITIALIZED_VARIABLE.on((JetSimpleNameExpression) element, variableDescriptor), ctxt); 257 } 258 } 259 } 260 261 private boolean checkValReassignment( 262 @NotNull VariableInitContext ctxt, 263 @NotNull JetExpression expression, 264 @NotNull Collection<VariableDescriptor> varWithValReassignErrorGenerated 265 ) { 266 VariableDescriptor variableDescriptor = ctxt.variableDescriptor; 267 if (JetPsiUtil.isBackingFieldReference(expression) && variableDescriptor instanceof PropertyDescriptor) { 268 PropertyDescriptor propertyDescriptor = (PropertyDescriptor) variableDescriptor; 269 JetPropertyAccessor accessor = PsiTreeUtil.getParentOfType(expression, JetPropertyAccessor.class); 270 if (accessor != null) { 271 DeclarationDescriptor accessorDescriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, accessor); 272 if (propertyDescriptor.getGetter() == accessorDescriptor) { 273 //val can be reassigned through backing field inside its own getter 274 return false; 275 } 276 } 277 } 278 279 boolean isInitializedNotHere = ctxt.enterInitState.isInitialized; 280 boolean hasBackingField = true; 281 if (variableDescriptor instanceof PropertyDescriptor) { 282 hasBackingField = trace.get(BindingContext.BACKING_FIELD_REQUIRED, (PropertyDescriptor) variableDescriptor); 283 } 284 if (variableDescriptor.isVar() && variableDescriptor instanceof PropertyDescriptor) { 285 DeclarationDescriptor descriptor = BindingContextUtils.getEnclosingDescriptor(trace.getBindingContext(), expression); 286 PropertySetterDescriptor setterDescriptor = ((PropertyDescriptor) variableDescriptor).getSetter(); 287 if (Visibilities.isVisible(variableDescriptor, descriptor) && !Visibilities.isVisible(setterDescriptor, descriptor) && setterDescriptor != null) { 288 report(Errors.INVISIBLE_SETTER.on(expression, variableDescriptor, setterDescriptor.getVisibility(), 289 variableDescriptor.getContainingDeclaration()), ctxt); 290 return true; 291 } 292 } 293 if ((isInitializedNotHere || !hasBackingField) && !variableDescriptor.isVar() && !varWithValReassignErrorGenerated.contains(variableDescriptor)) { 294 boolean hasReassignMethodReturningUnit = false; 295 JetSimpleNameExpression operationReference = null; 296 PsiElement parent = expression.getParent(); 297 if (parent instanceof JetBinaryExpression) { 298 operationReference = ((JetBinaryExpression) parent).getOperationReference(); 299 } 300 else if (parent instanceof JetUnaryExpression) { 301 operationReference = ((JetUnaryExpression) parent).getOperationReference(); 302 } 303 if (operationReference != null) { 304 DeclarationDescriptor descriptor = trace.get(BindingContext.REFERENCE_TARGET, operationReference); 305 if (descriptor instanceof FunctionDescriptor) { 306 if (KotlinBuiltIns.getInstance().isUnit(((FunctionDescriptor) descriptor).getReturnType())) { 307 hasReassignMethodReturningUnit = true; 308 } 309 } 310 if (descriptor == null) { 311 Collection<? extends DeclarationDescriptor> descriptors = trace.get(BindingContext.AMBIGUOUS_REFERENCE_TARGET, operationReference); 312 if (descriptors != null) { 313 for (DeclarationDescriptor referenceDescriptor : descriptors) { 314 if (KotlinBuiltIns.getInstance().isUnit(((FunctionDescriptor) referenceDescriptor).getReturnType())) { 315 hasReassignMethodReturningUnit = true; 316 } 317 } 318 } 319 } 320 } 321 if (!hasReassignMethodReturningUnit) { 322 varWithValReassignErrorGenerated.add(variableDescriptor); 323 report(Errors.VAL_REASSIGNMENT.on(expression, variableDescriptor), ctxt); 324 return true; 325 } 326 } 327 return false; 328 } 329 330 private boolean checkAssignmentBeforeDeclaration(@NotNull VariableInitContext ctxt, @NotNull JetExpression expression) { 331 if (!ctxt.enterInitState.isDeclared && !ctxt.exitInitState.isDeclared && !ctxt.enterInitState.isInitialized && ctxt.exitInitState.isInitialized) { 332 report(Errors.INITIALIZATION_BEFORE_DECLARATION.on(expression, ctxt.variableDescriptor), ctxt); 333 return true; 334 } 335 return false; 336 } 337 338 private boolean checkInitializationUsingBackingField(@NotNull VariableInitContext ctxt, @NotNull JetExpression expression) { 339 VariableDescriptor variableDescriptor = ctxt.variableDescriptor; 340 if (variableDescriptor instanceof PropertyDescriptor && !ctxt.enterInitState.isInitialized && ctxt.exitInitState.isInitialized) { 341 if (!variableDescriptor.isVar()) return false; 342 if (!trace.get(BindingContext.BACKING_FIELD_REQUIRED, (PropertyDescriptor) variableDescriptor)) return false; 343 PsiElement property = BindingContextUtils.descriptorToDeclaration(trace.getBindingContext(), variableDescriptor); 344 assert property instanceof JetProperty; 345 if (((PropertyDescriptor) variableDescriptor).getModality() == Modality.FINAL && ((JetProperty) property).getSetter() == null) return false; 346 JetExpression variable = expression; 347 if (expression instanceof JetDotQualifiedExpression) { 348 if (((JetDotQualifiedExpression) expression).getReceiverExpression() instanceof JetThisExpression) { 349 variable = ((JetDotQualifiedExpression) expression).getSelectorExpression(); 350 } 351 } 352 if (variable instanceof JetSimpleNameExpression) { 353 JetSimpleNameExpression simpleNameExpression = (JetSimpleNameExpression) variable; 354 if (simpleNameExpression.getReferencedNameElementType() != JetTokens.FIELD_IDENTIFIER) { 355 if (((PropertyDescriptor) variableDescriptor).getModality() != Modality.FINAL) { 356 report(Errors.INITIALIZATION_USING_BACKING_FIELD_OPEN_SETTER.on(expression, variableDescriptor), ctxt); 357 } 358 else { 359 report(Errors.INITIALIZATION_USING_BACKING_FIELD_CUSTOM_SETTER.on(expression, variableDescriptor), ctxt); 360 } 361 return true; 362 } 363 } 364 } 365 return false; 366 } 367 368 private boolean checkBackingField(@NotNull VariableContext cxtx, @NotNull JetElement element) { 369 VariableDescriptor variableDescriptor = cxtx.variableDescriptor; 370 boolean[] error = new boolean[1]; 371 if (isCorrectBackingFieldReference((JetElement) element.getParent(), cxtx, error, false)) return false; // this expression has been already checked 372 if (!isCorrectBackingFieldReference(element, cxtx, error, true)) return false; 373 if (error[0]) return true; 374 if (!(variableDescriptor instanceof PropertyDescriptor)) { 375 report(Errors.NOT_PROPERTY_BACKING_FIELD.on(element), cxtx); 376 return true; 377 } 378 PsiElement property = BindingContextUtils.descriptorToDeclaration(trace.getBindingContext(), variableDescriptor); 379 boolean insideSelfAccessors = PsiTreeUtil.isAncestor(property, element, false); 380 if (!trace.get(BindingContext.BACKING_FIELD_REQUIRED, (PropertyDescriptor) variableDescriptor) && 381 !insideSelfAccessors) { // not to generate error in accessors of abstract properties, there is one: declared accessor of abstract property 382 383 if (((PropertyDescriptor) variableDescriptor).getModality() == Modality.ABSTRACT) { 384 report(NO_BACKING_FIELD_ABSTRACT_PROPERTY.on(element), cxtx); 385 } 386 else { 387 report(NO_BACKING_FIELD_CUSTOM_ACCESSORS.on(element), cxtx); 388 } 389 return true; 390 } 391 if (insideSelfAccessors) return false; 392 393 DeclarationDescriptor declarationDescriptor = BindingContextUtils.getEnclosingDescriptor(trace.getBindingContext(), element); 394 395 DeclarationDescriptor containingDeclaration = variableDescriptor.getContainingDeclaration(); 396 if ((containingDeclaration instanceof ClassDescriptor) && DescriptorUtils.isAncestor(containingDeclaration, declarationDescriptor, false)) { 397 return false; 398 } 399 report(Errors.INACCESSIBLE_BACKING_FIELD.on(element), cxtx); 400 return true; 401 } 402 403 private boolean isCorrectBackingFieldReference(@Nullable JetElement element, VariableContext ctxt, boolean[] error, boolean reportError) { 404 error[0] = false; 405 if (JetPsiUtil.isBackingFieldReference(element)) { 406 return true; 407 } 408 if (element instanceof JetDotQualifiedExpression && isCorrectBackingFieldReference( 409 ((JetDotQualifiedExpression) element).getSelectorExpression(), ctxt, error, false)) { 410 if (((JetDotQualifiedExpression) element).getReceiverExpression() instanceof JetThisExpression) { 411 return true; 412 } 413 error[0] = true; 414 if (reportError) { 415 report(Errors.INACCESSIBLE_BACKING_FIELD.on(element), ctxt); 416 } 417 } 418 return false; 419 } 420 421 private void recordInitializedVariables(@NotNull Pseudocode pseudocode, @NotNull Map<Instruction, Edges<Map<VariableDescriptor,PseudocodeVariablesData.VariableInitState>>> initializersMap) { 422 Edges<Map<VariableDescriptor, VariableInitState>> initializers = initializersMap.get(pseudocode.getExitInstruction()); 423 Set<VariableDescriptor> declaredVariables = pseudocodeVariablesData.getDeclaredVariables(pseudocode, false); 424 for (VariableDescriptor variable : declaredVariables) { 425 if (variable instanceof PropertyDescriptor) { 426 PseudocodeVariablesData.VariableInitState variableInitState = initializers.in.get(variable); 427 if (variableInitState == null) return; 428 trace.record(BindingContext.IS_INITIALIZED, (PropertyDescriptor) variable, variableInitState.isInitialized); 429 } 430 } 431 } 432 433//////////////////////////////////////////////////////////////////////////////// 434// "Unused variable" & "unused value" analyses 435 436 public void markUnusedVariables() { 437 Map<Instruction, Edges<Map<VariableDescriptor, VariableUseState>>> variableStatusData = pseudocodeVariablesData.getVariableUseStatusData(); 438 final Map<Instruction, AbstractDiagnosticFactory> reportedDiagnosticMap = Maps.newHashMap(); 439 InstructionDataAnalyzeStrategy<Map<VariableDescriptor, VariableUseState>> variableStatusAnalyzeStrategy = 440 new InstructionDataAnalyzeStrategy<Map<VariableDescriptor, PseudocodeVariablesData.VariableUseState>>() { 441 @Override 442 public void execute(@NotNull Instruction instruction, 443 @Nullable Map<VariableDescriptor, VariableUseState> in, 444 @Nullable Map<VariableDescriptor, VariableUseState> out) { 445 446 assert in != null && out != null; 447 VariableContext ctxt = new VariableUseContext(instruction, reportedDiagnosticMap, in, out); 448 Set<VariableDescriptor> declaredVariables = pseudocodeVariablesData.getDeclaredVariables(instruction.getOwner(), false); 449 VariableDescriptor variableDescriptor = PseudocodeUtil.extractVariableDescriptorIfAny(instruction, false, 450 trace.getBindingContext()); 451 if (variableDescriptor == null || !declaredVariables.contains(variableDescriptor) || 452 !DescriptorUtils.isLocal(variableDescriptor.getContainingDeclaration(), variableDescriptor)) return; 453 PseudocodeVariablesData.VariableUseState variableUseState = in.get(variableDescriptor); 454 if (instruction instanceof WriteValueInstruction) { 455 if (trace.get(CAPTURED_IN_CLOSURE, variableDescriptor) != null) return; 456 JetElement element = ((WriteValueInstruction) instruction).getElement(); 457 if (variableUseState != LAST_READ) { 458 if (element instanceof JetBinaryExpression && 459 ((JetBinaryExpression) element).getOperationToken() == JetTokens.EQ) { 460 JetExpression right = ((JetBinaryExpression) element).getRight(); 461 if (right != null) { 462 report(Errors.UNUSED_VALUE.on(right, right, variableDescriptor), ctxt); 463 } 464 } 465 else if (element instanceof JetPostfixExpression) { 466 IElementType operationToken = ((JetPostfixExpression) element).getOperationReference().getReferencedNameElementType(); 467 if (operationToken == JetTokens.PLUSPLUS || operationToken == JetTokens.MINUSMINUS) { 468 report(Errors.UNUSED_CHANGED_VALUE.on(element, element), ctxt); 469 } 470 } 471 } 472 } 473 else if (instruction instanceof VariableDeclarationInstruction) { 474 JetDeclaration element = ((VariableDeclarationInstruction) instruction).getVariableDeclarationElement(); 475 if (!(element instanceof JetNamedDeclaration)) return; 476 PsiElement nameIdentifier = ((JetNamedDeclaration) element).getNameIdentifier(); 477 if (nameIdentifier == null) return; 478 if (!VariableUseState.isUsed(variableUseState)) { 479 if (JetPsiUtil.isVariableNotParameterDeclaration(element)) { 480 report(Errors.UNUSED_VARIABLE.on((JetNamedDeclaration) element, variableDescriptor), ctxt); 481 } 482 else if (element instanceof JetParameter) { 483 PsiElement psiElement = element.getParent().getParent(); 484 if (psiElement instanceof JetFunction) { 485 boolean isMain = (psiElement instanceof JetNamedFunction) && JetMainDetector.isMain((JetNamedFunction) psiElement); 486 if (psiElement instanceof JetFunctionLiteral) return; 487 DeclarationDescriptor descriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, psiElement); 488 assert descriptor instanceof FunctionDescriptor : psiElement.getText(); 489 FunctionDescriptor functionDescriptor = (FunctionDescriptor) descriptor; 490 if (!isMain && !functionDescriptor.getModality().isOverridable() && functionDescriptor.getOverriddenDescriptors().isEmpty()) { 491 report(Errors.UNUSED_PARAMETER.on((JetParameter) element, variableDescriptor), ctxt); 492 } 493 } 494 } 495 } 496 else if (variableUseState == ONLY_WRITTEN_NEVER_READ && JetPsiUtil.isVariableNotParameterDeclaration(element)) { 497 report(Errors.ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE.on((JetNamedDeclaration) element, variableDescriptor), ctxt); 498 } 499 else if (variableUseState == LAST_WRITTEN && element instanceof JetVariableDeclaration) { 500 if (element instanceof JetProperty) { 501 JetExpression initializer = ((JetProperty) element).getInitializer(); 502 if (initializer != null) { 503 report(Errors.VARIABLE_WITH_REDUNDANT_INITIALIZER.on(initializer, variableDescriptor), ctxt); 504 } 505 } 506 else if (element instanceof JetMultiDeclarationEntry) { 507 report(VARIABLE_WITH_REDUNDANT_INITIALIZER.on(element, variableDescriptor), ctxt); 508 } 509 } 510 } 511 } 512 }; 513 PseudocodeTraverser.traverse(pseudocode, BACKWARD, variableStatusData, variableStatusAnalyzeStrategy); 514 } 515 516//////////////////////////////////////////////////////////////////////////////// 517// "Unused literals" in block 518 519 public void markUnusedLiteralsInBlock() { 520 assert pseudocode != null; 521 final Map<Instruction, AbstractDiagnosticFactory> reportedDiagnosticMap = Maps.newHashMap(); 522 PseudocodeTraverser.traverse( 523 pseudocode, FORWARD, new InstructionAnalyzeStrategy() { 524 @Override 525 public void execute(@NotNull Instruction instruction) { 526 if (!(instruction instanceof ReadValueInstruction)) return; 527 VariableContext ctxt = new VariableContext(instruction, reportedDiagnosticMap); 528 JetElement element = 529 ((ReadValueInstruction) instruction).getElement(); 530 if (!(element instanceof JetFunctionLiteralExpression 531 || element instanceof JetConstantExpression 532 || element instanceof JetStringTemplateExpression 533 || element instanceof JetSimpleNameExpression)) { 534 return; 535 } 536 PsiElement parent = element.getParent(); 537 if (parent instanceof JetBlockExpression) { 538 if (!JetPsiUtil.isImplicitlyUsed(element)) { 539 if (element instanceof JetFunctionLiteralExpression) { 540 report(Errors.UNUSED_FUNCTION_LITERAL.on((JetFunctionLiteralExpression) element), ctxt); 541 } 542 else { 543 report(Errors.UNUSED_EXPRESSION.on(element), ctxt); 544 } 545 } 546 } 547 } 548 }); 549 } 550 551//////////////////////////////////////////////////////////////////////////////// 552// Utility classes and methods 553 554 /** 555 * The method provides reporting of the same diagnostic only once for copied instructions 556 * (depends on whether it should be reported for all or only for one of the copies) 557 */ 558 private void report( 559 @NotNull Diagnostic diagnostic, 560 @NotNull VariableContext ctxt 561 ) { 562 Instruction instruction = ctxt.instruction; 563 if (instruction.getCopies().isEmpty()) { 564 trace.report(diagnostic); 565 return; 566 } 567 Map<Instruction, AbstractDiagnosticFactory> previouslyReported = ctxt.reportedDiagnosticMap; 568 previouslyReported.put(instruction, diagnostic.getFactory()); 569 570 boolean alreadyReported = false; 571 boolean sameErrorForAllCopies = true; 572 for (Instruction copy : instruction.getCopies()) { 573 AbstractDiagnosticFactory previouslyReportedErrorFactory = previouslyReported.get(copy); 574 if (previouslyReportedErrorFactory != null) { 575 alreadyReported = true; 576 } 577 578 if (previouslyReportedErrorFactory != diagnostic.getFactory()) { 579 sameErrorForAllCopies = false; 580 } 581 } 582 583 if (mustBeReportedOnAllCopies(diagnostic.getFactory())) { 584 if (sameErrorForAllCopies) { 585 trace.report(diagnostic); 586 } 587 } 588 else { 589 //only one reporting required 590 if (!alreadyReported) { 591 trace.report(diagnostic); 592 } 593 } 594 } 595 596 private static boolean mustBeReportedOnAllCopies(@NotNull AbstractDiagnosticFactory diagnosticFactory) { 597 return diagnosticFactory == UNUSED_VARIABLE 598 || diagnosticFactory == UNUSED_PARAMETER 599 || diagnosticFactory == UNUSED_CHANGED_VALUE; 600 } 601 602 603 604 private class VariableContext { 605 final Map<Instruction, AbstractDiagnosticFactory> reportedDiagnosticMap; 606 final Instruction instruction; 607 final VariableDescriptor variableDescriptor; 608 609 private VariableContext( 610 @NotNull Instruction instruction, 611 @NotNull Map<Instruction, AbstractDiagnosticFactory> map 612 ) { 613 this.instruction = instruction; 614 reportedDiagnosticMap = map; 615 variableDescriptor = PseudocodeUtil.extractVariableDescriptorIfAny(instruction, true, trace.getBindingContext()); 616 } 617 } 618 619 private class VariableInitContext extends VariableContext { 620 final VariableInitState enterInitState; 621 final VariableInitState exitInitState; 622 623 private VariableInitContext( 624 @NotNull Instruction instruction, 625 @NotNull Map<Instruction, AbstractDiagnosticFactory> map, 626 @NotNull Map<VariableDescriptor, VariableInitState> in, 627 @NotNull Map<VariableDescriptor, VariableInitState> out 628 ) { 629 super(instruction, map); 630 enterInitState = variableDescriptor != null ? in.get(variableDescriptor) : null; 631 exitInitState = variableDescriptor != null ? out.get(variableDescriptor) : null; 632 } 633 } 634 635 private class VariableUseContext extends VariableContext { 636 final VariableUseState enterUseState; 637 final VariableUseState exitUseState; 638 639 640 private VariableUseContext( 641 @NotNull Instruction instruction, 642 @NotNull Map<Instruction, AbstractDiagnosticFactory> map, 643 @NotNull Map<VariableDescriptor, VariableUseState> in, 644 @NotNull Map<VariableDescriptor, VariableUseState> out 645 ) { 646 super(instruction, map); 647 enterUseState = variableDescriptor != null ? in.get(variableDescriptor) : null; 648 exitUseState = variableDescriptor != null ? out.get(variableDescriptor) : null; 649 } 650 } 651}