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