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