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