001 /*
002 * Copyright 2010-2013 JetBrains s.r.o.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017 package org.jetbrains.jet.lang.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.Function1;
026 import kotlin.Function3;
027 import kotlin.Unit;
028 import org.jetbrains.annotations.NotNull;
029 import org.jetbrains.annotations.Nullable;
030 import org.jetbrains.jet.lang.cfg.PseudocodeVariablesData.VariableInitState;
031 import org.jetbrains.jet.lang.cfg.PseudocodeVariablesData.VariableUseState;
032 import org.jetbrains.jet.lang.cfg.pseudocode.PseudoValue;
033 import org.jetbrains.jet.lang.cfg.pseudocode.Pseudocode;
034 import org.jetbrains.jet.lang.cfg.pseudocode.PseudocodePackage;
035 import org.jetbrains.jet.lang.cfg.pseudocode.PseudocodeUtil;
036 import org.jetbrains.jet.lang.cfg.pseudocode.instructions.Instruction;
037 import org.jetbrains.jet.lang.cfg.pseudocode.instructions.InstructionVisitor;
038 import org.jetbrains.jet.lang.cfg.pseudocode.instructions.JetElementInstruction;
039 import org.jetbrains.jet.lang.cfg.pseudocode.instructions.eval.*;
040 import org.jetbrains.jet.lang.cfg.pseudocode.instructions.jumps.*;
041 import org.jetbrains.jet.lang.cfg.pseudocode.instructions.special.LocalFunctionDeclarationInstruction;
042 import org.jetbrains.jet.lang.cfg.pseudocode.instructions.special.MarkInstruction;
043 import org.jetbrains.jet.lang.cfg.pseudocode.instructions.special.SubroutineExitInstruction;
044 import org.jetbrains.jet.lang.cfg.pseudocode.instructions.special.VariableDeclarationInstruction;
045 import org.jetbrains.jet.lang.cfg.pseudocodeTraverser.Edges;
046 import org.jetbrains.jet.lang.cfg.pseudocodeTraverser.PseudocodeTraverserPackage;
047 import org.jetbrains.jet.lang.cfg.pseudocodeTraverser.TraversalOrder;
048 import org.jetbrains.jet.lang.descriptors.*;
049 import org.jetbrains.jet.lang.diagnostics.Diagnostic;
050 import org.jetbrains.jet.lang.diagnostics.DiagnosticFactory;
051 import org.jetbrains.jet.lang.diagnostics.Errors;
052 import org.jetbrains.jet.lang.psi.*;
053 import org.jetbrains.jet.lang.resolve.*;
054 import org.jetbrains.jet.lang.resolve.bindingContextUtil.BindingContextUtilPackage;
055 import org.jetbrains.jet.lang.resolve.calls.TailRecursionKind;
056 import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
057 import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver;
058 import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue;
059 import org.jetbrains.jet.lang.resolve.scopes.receivers.ThisReceiver;
060 import org.jetbrains.jet.lang.types.JetType;
061 import org.jetbrains.jet.lang.types.expressions.ExpressionTypingUtils;
062 import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
063 import org.jetbrains.jet.lexer.JetTokens;
064 import org.jetbrains.jet.plugin.MainFunctionDetector;
065
066 import java.util.*;
067
068 import static org.jetbrains.jet.lang.cfg.PseudocodeVariablesData.VariableUseState.*;
069 import static org.jetbrains.jet.lang.cfg.pseudocodeTraverser.TraversalOrder.FORWARD;
070 import static org.jetbrains.jet.lang.diagnostics.Errors.*;
071 import static org.jetbrains.jet.lang.resolve.BindingContext.CAPTURED_IN_CLOSURE;
072 import static org.jetbrains.jet.lang.resolve.BindingContext.TAIL_RECURSION_CALL;
073 import static org.jetbrains.jet.lang.resolve.calls.TailRecursionKind.*;
074 import static org.jetbrains.jet.lang.resolve.calls.callUtil.CallUtilPackage.getResolvedCall;
075 import static org.jetbrains.jet.lang.types.TypeUtils.NO_EXPECTED_TYPE;
076 import static org.jetbrains.jet.lang.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(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(ReturnNoValueInstruction instruction) {
158 if (instructions.contains(instruction)) {
159 returnedExpressions.add(instruction.getElement());
160 }
161 }
162
163
164 @Override
165 public void visitJump(AbstractJumpInstruction instruction) {
166 // Nothing
167 }
168
169 @Override
170 public void visitUnconditionalJump(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(NondeterministicJumpInstruction instruction) {
182 redirectToPrevInstructions(instruction);
183 }
184
185 @Override
186 public void visitMarkInstruction(MarkInstruction instruction) {
187 redirectToPrevInstructions(instruction);
188 }
189
190 @Override
191 public void visitInstruction(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.getInstance().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 }
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 JetElement element = ((ReadValueInstruction) instruction).getElement();
327 boolean error = checkBackingField(ctxt, element);
328 if (!error && declaredVariables.contains(ctxt.variableDescriptor)) {
329 checkIsInitialized(ctxt, element, varWithUninitializedErrorGenerated);
330 }
331 return;
332 }
333 if (!(instruction instanceof WriteValueInstruction)) return;
334 JetElement element = ((WriteValueInstruction) instruction).getlValue();
335 boolean error = checkBackingField(ctxt, element);
336 if (!(element instanceof JetExpression)) return;
337 if (!error) {
338 error = checkValReassignment(ctxt, (JetExpression) element, varWithValReassignErrorGenerated);
339 }
340 if (!error && processClassOrObject) {
341 error = checkAssignmentBeforeDeclaration(ctxt, (JetExpression) element);
342 }
343 if (!error && processClassOrObject) {
344 checkInitializationUsingBackingField(ctxt, (JetExpression) 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, VariableInitState>>> 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 JetElement element,
365 @NotNull Collection<VariableDescriptor> varWithUninitializedErrorGenerated
366 ) {
367 if (!(element instanceof JetSimpleNameExpression)) return;
368
369 boolean isInitialized = ctxt.exitInitState.isInitialized;
370 VariableDescriptor variableDescriptor = ctxt.variableDescriptor;
371 if (variableDescriptor instanceof PropertyDescriptor) {
372 if (!trace.get(BindingContext.BACKING_FIELD_REQUIRED, (PropertyDescriptor) variableDescriptor)) {
373 isInitialized = true;
374 }
375 }
376 if (!isInitialized && !varWithUninitializedErrorGenerated.contains(variableDescriptor)) {
377 if (!(variableDescriptor instanceof PropertyDescriptor)) {
378 varWithUninitializedErrorGenerated.add(variableDescriptor);
379 }
380 if (variableDescriptor instanceof ValueParameterDescriptor) {
381 report(Errors.UNINITIALIZED_PARAMETER.on((JetSimpleNameExpression) element,
382 (ValueParameterDescriptor) variableDescriptor), ctxt);
383 }
384 else {
385 report(Errors.UNINITIALIZED_VARIABLE.on((JetSimpleNameExpression) element, variableDescriptor), ctxt);
386 }
387 }
388 }
389
390 private boolean checkValReassignment(
391 @NotNull VariableInitContext ctxt,
392 @NotNull JetExpression expression,
393 @NotNull Collection<VariableDescriptor> varWithValReassignErrorGenerated
394 ) {
395 VariableDescriptor variableDescriptor = ctxt.variableDescriptor;
396 if (JetPsiUtil.isBackingFieldReference(expression) && variableDescriptor instanceof PropertyDescriptor) {
397 PropertyDescriptor propertyDescriptor = (PropertyDescriptor) variableDescriptor;
398 JetPropertyAccessor accessor = PsiTreeUtil.getParentOfType(expression, JetPropertyAccessor.class);
399 if (accessor != null) {
400 DeclarationDescriptor accessorDescriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, accessor);
401 if (propertyDescriptor.getGetter() == accessorDescriptor) {
402 //val can be reassigned through backing field inside its own getter
403 return false;
404 }
405 }
406 }
407
408 boolean isInitializedNotHere = ctxt.enterInitState.isInitialized;
409 boolean hasBackingField = true;
410 if (variableDescriptor instanceof PropertyDescriptor) {
411 hasBackingField = trace.get(BindingContext.BACKING_FIELD_REQUIRED, (PropertyDescriptor) variableDescriptor);
412 }
413 if (variableDescriptor.isVar() && variableDescriptor instanceof PropertyDescriptor) {
414 DeclarationDescriptor descriptor = BindingContextUtils.getEnclosingDescriptor(trace.getBindingContext(), expression);
415 PropertySetterDescriptor setterDescriptor = ((PropertyDescriptor) variableDescriptor).getSetter();
416 if (Visibilities.isVisible(variableDescriptor, descriptor) && setterDescriptor != null
417 && !Visibilities.isVisible(setterDescriptor, descriptor)) {
418 report(Errors.INVISIBLE_SETTER.on(expression, variableDescriptor, setterDescriptor.getVisibility(),
419 variableDescriptor.getContainingDeclaration()), ctxt);
420 return true;
421 }
422 }
423 if ((isInitializedNotHere || !hasBackingField) && !variableDescriptor.isVar()
424 && !varWithValReassignErrorGenerated.contains(variableDescriptor)) {
425 boolean hasReassignMethodReturningUnit = false;
426 JetSimpleNameExpression operationReference = null;
427 PsiElement parent = expression.getParent();
428 if (parent instanceof JetBinaryExpression) {
429 operationReference = ((JetBinaryExpression) parent).getOperationReference();
430 }
431 else if (parent instanceof JetUnaryExpression) {
432 operationReference = ((JetUnaryExpression) parent).getOperationReference();
433 }
434 if (operationReference != null) {
435 DeclarationDescriptor descriptor = trace.get(BindingContext.REFERENCE_TARGET, operationReference);
436 if (descriptor instanceof FunctionDescriptor) {
437 if (KotlinBuiltIns.getInstance().isUnit(((FunctionDescriptor) descriptor).getReturnType())) {
438 hasReassignMethodReturningUnit = true;
439 }
440 }
441 if (descriptor == null) {
442 Collection<? extends DeclarationDescriptor> descriptors =
443 trace.get(BindingContext.AMBIGUOUS_REFERENCE_TARGET, operationReference);
444 if (descriptors != null) {
445 for (DeclarationDescriptor referenceDescriptor : descriptors) {
446 if (KotlinBuiltIns.getInstance().isUnit(((FunctionDescriptor) referenceDescriptor).getReturnType())) {
447 hasReassignMethodReturningUnit = true;
448 }
449 }
450 }
451 }
452 }
453 if (!hasReassignMethodReturningUnit) {
454 varWithValReassignErrorGenerated.add(variableDescriptor);
455 report(Errors.VAL_REASSIGNMENT.on(expression, variableDescriptor), ctxt);
456 return true;
457 }
458 }
459 return false;
460 }
461
462 private boolean checkAssignmentBeforeDeclaration(@NotNull VariableInitContext ctxt, @NotNull JetExpression expression) {
463 if (!ctxt.enterInitState.isDeclared && !ctxt.exitInitState.isDeclared
464 && !ctxt.enterInitState.isInitialized && ctxt.exitInitState.isInitialized) {
465 report(Errors.INITIALIZATION_BEFORE_DECLARATION.on(expression, ctxt.variableDescriptor), ctxt);
466 return true;
467 }
468 return false;
469 }
470
471 private boolean checkInitializationUsingBackingField(@NotNull VariableInitContext ctxt, @NotNull JetExpression expression) {
472 VariableDescriptor variableDescriptor = ctxt.variableDescriptor;
473 if (variableDescriptor instanceof PropertyDescriptor && !ctxt.enterInitState.isInitialized && ctxt.exitInitState.isInitialized) {
474 if (!variableDescriptor.isVar()) return false;
475 if (!trace.get(BindingContext.BACKING_FIELD_REQUIRED, (PropertyDescriptor) variableDescriptor)) return false;
476 PsiElement property = DescriptorToSourceUtils.descriptorToDeclaration(variableDescriptor);
477 assert property instanceof JetProperty;
478 if (((PropertyDescriptor) variableDescriptor).getModality() == Modality.FINAL && ((JetProperty) property).getSetter() == null) {
479 return false;
480 }
481 JetExpression variable = expression;
482 if (expression instanceof JetDotQualifiedExpression) {
483 if (((JetDotQualifiedExpression) expression).getReceiverExpression() instanceof JetThisExpression) {
484 variable = ((JetDotQualifiedExpression) expression).getSelectorExpression();
485 }
486 }
487 if (variable instanceof JetSimpleNameExpression) {
488 JetSimpleNameExpression simpleNameExpression = (JetSimpleNameExpression) variable;
489 if (simpleNameExpression.getReferencedNameElementType() != JetTokens.FIELD_IDENTIFIER) {
490 if (((PropertyDescriptor) variableDescriptor).getModality() != Modality.FINAL) {
491 report(Errors.INITIALIZATION_USING_BACKING_FIELD_OPEN_SETTER.on(expression, variableDescriptor), ctxt);
492 }
493 else {
494 report(Errors.INITIALIZATION_USING_BACKING_FIELD_CUSTOM_SETTER.on(expression, variableDescriptor), ctxt);
495 }
496 return true;
497 }
498 }
499 }
500 return false;
501 }
502
503 private boolean checkBackingField(@NotNull VariableContext cxtx, @NotNull JetElement element) {
504 VariableDescriptor variableDescriptor = cxtx.variableDescriptor;
505 boolean[] error = new boolean[1];
506 if (!isCorrectBackingFieldReference(element, cxtx, error, true)) return false;
507 if (error[0]) return true;
508 if (!(variableDescriptor instanceof PropertyDescriptor)) {
509 report(Errors.NOT_PROPERTY_BACKING_FIELD.on(element), cxtx);
510 return true;
511 }
512 PsiElement property = DescriptorToSourceUtils.descriptorToDeclaration(variableDescriptor);
513 boolean insideSelfAccessors = PsiTreeUtil.isAncestor(property, element, false);
514 if (!trace.get(BindingContext.BACKING_FIELD_REQUIRED, (PropertyDescriptor) variableDescriptor) &&
515 // not to generate error in accessors of abstract properties, there is one: declared accessor of abstract property
516 !insideSelfAccessors) {
517
518 if (((PropertyDescriptor) variableDescriptor).getModality() == Modality.ABSTRACT) {
519 report(NO_BACKING_FIELD_ABSTRACT_PROPERTY.on(element), cxtx);
520 }
521 else {
522 report(NO_BACKING_FIELD_CUSTOM_ACCESSORS.on(element), cxtx);
523 }
524 return true;
525 }
526 if (insideSelfAccessors) return false;
527
528 DeclarationDescriptor declarationDescriptor = BindingContextUtils.getEnclosingDescriptor(trace.getBindingContext(), element);
529
530 DeclarationDescriptor containingDeclaration = variableDescriptor.getContainingDeclaration();
531 if ((containingDeclaration instanceof ClassDescriptor)
532 && DescriptorUtils.isAncestor(containingDeclaration, declarationDescriptor, false)) {
533 return false;
534 }
535 report(Errors.INACCESSIBLE_BACKING_FIELD.on(element), cxtx);
536 return true;
537 }
538
539 private boolean isCorrectBackingFieldReference(
540 @Nullable JetElement element,
541 VariableContext ctxt,
542 boolean[] error,
543 boolean reportError
544 ) {
545 error[0] = false;
546 if (JetPsiUtil.isBackingFieldReference(element)) {
547 return true;
548 }
549 if (element instanceof JetDotQualifiedExpression && isCorrectBackingFieldReference(
550 ((JetDotQualifiedExpression) element).getSelectorExpression(), ctxt, error, false)) {
551 if (((JetDotQualifiedExpression) element).getReceiverExpression() instanceof JetThisExpression) {
552 return true;
553 }
554 error[0] = true;
555 if (reportError) {
556 report(Errors.INACCESSIBLE_BACKING_FIELD.on(element), ctxt);
557 }
558 }
559 return false;
560 }
561
562 private void recordInitializedVariables(
563 @NotNull Pseudocode pseudocode,
564 @NotNull Map<Instruction, Edges<Map<VariableDescriptor, PseudocodeVariablesData.VariableInitState>>> initializersMap
565 ) {
566 Edges<Map<VariableDescriptor, VariableInitState>> initializers = initializersMap.get(pseudocode.getExitInstruction());
567 Set<VariableDescriptor> declaredVariables = getPseudocodeVariablesData().getDeclaredVariables(pseudocode, false);
568 for (VariableDescriptor variable : declaredVariables) {
569 if (variable instanceof PropertyDescriptor) {
570 PseudocodeVariablesData.VariableInitState variableInitState = initializers.getIncoming().get(variable);
571 if (variableInitState == null) return;
572 trace.record(BindingContext.IS_INITIALIZED, (PropertyDescriptor) variable, variableInitState.isInitialized);
573 }
574 }
575 }
576
577 ////////////////////////////////////////////////////////////////////////////////
578 // "Unused variable" & "unused value" analyses
579
580 public void markUnusedVariables() {
581 final PseudocodeVariablesData pseudocodeVariablesData = getPseudocodeVariablesData();
582 Map<Instruction, Edges<Map<VariableDescriptor, VariableUseState>>> variableStatusData =
583 pseudocodeVariablesData.getVariableUseStatusData();
584 final Map<Instruction, DiagnosticFactory<?>> reportedDiagnosticMap = Maps.newHashMap();
585 InstructionDataAnalyzeStrategy<Map<VariableDescriptor, VariableUseState>> variableStatusAnalyzeStrategy =
586 new InstructionDataAnalyzeStrategy<Map<VariableDescriptor, VariableUseState>>() {
587 @Override
588 public void execute(
589 @NotNull Instruction instruction,
590 @Nullable Map<VariableDescriptor, VariableUseState> in,
591 @Nullable Map<VariableDescriptor, VariableUseState> out
592 ) {
593
594 assert in != null && out != null;
595 VariableContext ctxt = new VariableUseContext(instruction, reportedDiagnosticMap, in, out);
596 Set<VariableDescriptor> declaredVariables =
597 pseudocodeVariablesData.getDeclaredVariables(instruction.getOwner(), false);
598 VariableDescriptor variableDescriptor = PseudocodeUtil.extractVariableDescriptorIfAny(
599 instruction, false, trace.getBindingContext());
600 if (variableDescriptor == null || !declaredVariables.contains(variableDescriptor)
601 || !ExpressionTypingUtils.isLocal(variableDescriptor.getContainingDeclaration(), variableDescriptor)) {
602 return;
603 }
604 PseudocodeVariablesData.VariableUseState variableUseState = in.get(variableDescriptor);
605 if (instruction instanceof WriteValueInstruction) {
606 if (trace.get(CAPTURED_IN_CLOSURE, variableDescriptor) != null) return;
607 JetElement element = ((WriteValueInstruction) instruction).getElement();
608 if (variableUseState != READ) {
609 if (element instanceof JetBinaryExpression &&
610 ((JetBinaryExpression) element).getOperationToken() == JetTokens.EQ) {
611 JetExpression right = ((JetBinaryExpression) element).getRight();
612 if (right != null) {
613 report(Errors.UNUSED_VALUE.on(right, right, variableDescriptor), ctxt);
614 }
615 }
616 else if (element instanceof JetPostfixExpression) {
617 IElementType operationToken =
618 ((JetPostfixExpression) element).getOperationReference().getReferencedNameElementType();
619 if (operationToken == JetTokens.PLUSPLUS || operationToken == JetTokens.MINUSMINUS) {
620 report(Errors.UNUSED_CHANGED_VALUE.on(element, element), ctxt);
621 }
622 }
623 }
624 }
625 else if (instruction instanceof VariableDeclarationInstruction) {
626 JetDeclaration element = ((VariableDeclarationInstruction) instruction).getVariableDeclarationElement();
627 if (!(element instanceof JetNamedDeclaration)) return;
628 PsiElement nameIdentifier = ((JetNamedDeclaration) element).getNameIdentifier();
629 if (nameIdentifier == null) return;
630 if (!VariableUseState.isUsed(variableUseState)) {
631 if (JetPsiUtil.isVariableNotParameterDeclaration(element)) {
632 report(Errors.UNUSED_VARIABLE.on((JetNamedDeclaration) element, variableDescriptor), ctxt);
633 }
634 else if (element instanceof JetParameter) {
635 PsiElement psiElement = element.getParent().getParent();
636 if (psiElement instanceof JetFunction) {
637 MainFunctionDetector mainFunctionDetector = new MainFunctionDetector(trace.getBindingContext());
638 boolean isMain = (psiElement instanceof JetNamedFunction) && mainFunctionDetector.isMain((JetNamedFunction) psiElement);
639 if (psiElement instanceof JetFunctionLiteral) return;
640 DeclarationDescriptor descriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, psiElement);
641 assert descriptor instanceof FunctionDescriptor : psiElement.getText();
642 FunctionDescriptor functionDescriptor = (FunctionDescriptor) descriptor;
643 if (!isMain && !functionDescriptor.getModality().isOverridable()
644 && functionDescriptor.getOverriddenDescriptors().isEmpty()) {
645 report(Errors.UNUSED_PARAMETER.on((JetParameter) element, variableDescriptor), ctxt);
646 }
647 }
648 }
649 }
650 else if (variableUseState == ONLY_WRITTEN_NEVER_READ && JetPsiUtil.isVariableNotParameterDeclaration(element)) {
651 report(Errors.ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE.on((JetNamedDeclaration) element, variableDescriptor), ctxt);
652 }
653 else if (variableUseState == WRITTEN_AFTER_READ && element instanceof JetVariableDeclaration) {
654 if (element instanceof JetProperty) {
655 JetExpression initializer = ((JetProperty) element).getInitializer();
656 if (initializer != null) {
657 report(Errors.VARIABLE_WITH_REDUNDANT_INITIALIZER.on(initializer, variableDescriptor), ctxt);
658 }
659 }
660 else if (element instanceof JetMultiDeclarationEntry) {
661 report(VARIABLE_WITH_REDUNDANT_INITIALIZER.on(element, variableDescriptor), ctxt);
662 }
663 }
664 }
665 }
666 };
667 PseudocodeTraverserPackage.traverse(pseudocode, TraversalOrder.BACKWARD, variableStatusData, variableStatusAnalyzeStrategy);
668 }
669
670 ////////////////////////////////////////////////////////////////////////////////
671 // "Unused expressions" in block
672
673 public void markUnusedExpressions() {
674 final Map<Instruction, DiagnosticFactory<?>> reportedDiagnosticMap = Maps.newHashMap();
675 PseudocodeTraverserPackage.traverse(
676 pseudocode, FORWARD, new JetFlowInformationProvider.FunctionVoid1<Instruction>() {
677 @Override
678 public void execute(@NotNull Instruction instruction) {
679 if (!(instruction instanceof JetElementInstruction)) return;
680
681 JetElement element = ((JetElementInstruction)instruction).getElement();
682 if (!(element instanceof JetExpression)) return;
683
684 if (BindingContextUtilPackage.isUsedAsStatement((JetExpression) element, trace.getBindingContext())
685 && PseudocodePackage.getSideEffectFree(instruction)) {
686 VariableContext ctxt = new VariableContext(instruction, reportedDiagnosticMap);
687 report(
688 element instanceof JetFunctionLiteralExpression
689 ? Errors.UNUSED_FUNCTION_LITERAL.on((JetFunctionLiteralExpression) element)
690 : Errors.UNUSED_EXPRESSION.on(element),
691 ctxt
692 );
693 }
694 }
695 }
696 );
697 }
698
699 ////////////////////////////////////////////////////////////////////////////////
700 // Statements
701
702 public void markStatements() {
703 PseudocodeTraverserPackage.traverse(
704 pseudocode, FORWARD, new JetFlowInformationProvider.FunctionVoid1<Instruction>() {
705 @Override
706 public void execute(@NotNull Instruction instruction) {
707 PseudoValue value = instruction instanceof InstructionWithValue
708 ? ((InstructionWithValue) instruction).getOutputValue()
709 : null;
710 Pseudocode pseudocode = instruction.getOwner();
711 boolean isUsedAsExpression = !pseudocode.getUsages(value).isEmpty();
712 for (JetElement element : pseudocode.getValueElements(value)) {
713 trace.record(BindingContext.USED_AS_EXPRESSION, element, isUsedAsExpression);
714 }
715 }
716 }
717 );
718 }
719
720 public void markWhenWithoutElse() {
721 PseudocodeTraverserPackage.traverse(
722 pseudocode, FORWARD, new JetFlowInformationProvider.FunctionVoid1<Instruction>() {
723 @Override
724 public void execute(@NotNull Instruction instruction) {
725 PseudoValue value = instruction instanceof InstructionWithValue
726 ? ((InstructionWithValue) instruction).getOutputValue()
727 : null;
728 for (JetElement element : instruction.getOwner().getValueElements(value)) {
729 if (element instanceof JetWhenExpression) {
730 JetWhenExpression whenExpression = (JetWhenExpression) element;
731 if (whenExpression.getElseExpression() == null && WhenChecker.mustHaveElse(whenExpression, trace)) {
732 trace.report(NO_ELSE_IN_WHEN.on(whenExpression));
733 }
734 }
735 }
736 }
737 }
738 );
739 }
740
741 ////////////////////////////////////////////////////////////////////////////////
742 // Tail calls
743
744 public void markTailCalls() {
745 final DeclarationDescriptor subroutineDescriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, subroutine);
746 if (!(subroutineDescriptor instanceof FunctionDescriptor)) return;
747 if (!KotlinBuiltIns.getInstance().isTailRecursive(subroutineDescriptor)) return;
748
749 // finally blocks are copied which leads to multiple diagnostics reported on one instruction
750 class KindAndCall {
751 TailRecursionKind kind;
752 ResolvedCall<?> call;
753
754 KindAndCall(TailRecursionKind kind, ResolvedCall<?> call) {
755 this.kind = kind;
756 this.call = call;
757 }
758 }
759 final Map<JetElement, KindAndCall> calls = new HashMap<JetElement, KindAndCall>();
760 PseudocodeTraverserPackage.traverse(
761 pseudocode,
762 FORWARD,
763 new FunctionVoid1<Instruction>() {
764 public void execute(@NotNull Instruction instruction) {
765 if (!(instruction instanceof CallInstruction)) return;
766 CallInstruction callInstruction = (CallInstruction) instruction;
767
768 ResolvedCall<?> resolvedCall = getResolvedCall(callInstruction.getElement(), trace.getBindingContext());
769 if (resolvedCall == null) return;
770
771 // is this a recursive call?
772 CallableDescriptor functionDescriptor = resolvedCall.getResultingDescriptor();
773 if (!functionDescriptor.getOriginal().equals(subroutineDescriptor)) return;
774
775 JetElement element = callInstruction.getElement();
776 //noinspection unchecked
777 JetExpression parent = PsiTreeUtil.getParentOfType(
778 element,
779 JetTryExpression.class, JetFunction.class, JetClassInitializer.class
780 );
781
782 if (parent instanceof JetTryExpression) {
783 // We do not support tail calls Collections.singletonMap() try-catch-finally, for simplicity of the mental model
784 // very few cases there would be real tail-calls, and it's often not so easy for the user to see why
785 calls.put(element, new KindAndCall(IN_TRY, resolvedCall));
786 return;
787 }
788
789 boolean isTail = PseudocodeTraverserPackage.traverseFollowingInstructions(
790 callInstruction,
791 new HashSet<Instruction>(),
792 FORWARD,
793 new TailRecursionDetector(subroutine, callInstruction)
794 );
795
796 boolean sameThisObject = sameThisObject(resolvedCall);
797
798 TailRecursionKind kind = isTail && sameThisObject ? TAIL_CALL : NON_TAIL;
799
800 KindAndCall kindAndCall = calls.get(element);
801 calls.put(element,
802 new KindAndCall(
803 combineKinds(kind, kindAndCall == null ? null : kindAndCall.kind),
804 resolvedCall
805 )
806 );
807 }
808 }
809 );
810 boolean hasTailCalls = false;
811 for (Map.Entry<JetElement, KindAndCall> entry : calls.entrySet()) {
812 JetElement element = entry.getKey();
813 KindAndCall kindAndCall = entry.getValue();
814 switch (kindAndCall.kind) {
815 case TAIL_CALL:
816 trace.record(TAIL_RECURSION_CALL, kindAndCall.call, TailRecursionKind.TAIL_CALL);
817 hasTailCalls = true;
818 break;
819 case IN_TRY:
820 trace.report(Errors.TAIL_RECURSION_IN_TRY_IS_NOT_SUPPORTED.on(element));
821 break;
822 case NON_TAIL:
823 trace.report(Errors.NON_TAIL_RECURSIVE_CALL.on(element));
824 break;
825 }
826 }
827
828 if (!hasTailCalls) {
829 trace.report(Errors.NO_TAIL_CALLS_FOUND.on((JetNamedFunction) subroutine));
830 }
831 }
832
833 private boolean sameThisObject(ResolvedCall<?> resolvedCall) {
834 // A tail call is not allowed to change dispatch receiver
835 // class C {
836 // fun foo(other: C) {
837 // other.foo(this) // not a tail call
838 // }
839 // }
840 ReceiverParameterDescriptor thisObject = resolvedCall.getResultingDescriptor().getExpectedThisObject();
841 ReceiverValue thisObjectValue = resolvedCall.getThisObject();
842 if (thisObject == null || !thisObjectValue.exists()) return true;
843
844 DeclarationDescriptor classDescriptor = null;
845 if (thisObjectValue instanceof ThisReceiver) {
846 // foo() -- implicit receiver
847 classDescriptor = ((ThisReceiver) thisObjectValue).getDeclarationDescriptor();
848 }
849 else if (thisObjectValue instanceof ExpressionReceiver) {
850 JetExpression expression = JetPsiUtil.deparenthesize(((ExpressionReceiver) thisObjectValue).getExpression());
851 if (expression instanceof JetThisExpression) {
852 // this.foo() -- explicit receiver
853 JetThisExpression thisExpression = (JetThisExpression) expression;
854 classDescriptor = trace.get(BindingContext.REFERENCE_TARGET, thisExpression.getInstanceReference());
855 }
856 }
857 return thisObject.getContainingDeclaration() == classDescriptor;
858 }
859
860 private static TailRecursionKind combineKinds(TailRecursionKind kind, @Nullable TailRecursionKind existingKind) {
861 TailRecursionKind resultingKind;
862 if (existingKind == null || existingKind == kind) {
863 resultingKind = kind;
864 }
865 else {
866 if (check(kind, existingKind, IN_TRY, TAIL_CALL)) {
867 resultingKind = IN_TRY;
868 }
869 else if (check(kind, existingKind, IN_TRY, NON_TAIL)) {
870 resultingKind = IN_TRY;
871 }
872 else {
873 // TAIL_CALL, NON_TAIL
874 resultingKind = NON_TAIL;
875 }
876 }
877 return resultingKind;
878 }
879
880 private static boolean check(Object a, Object b, Object x, Object y) {
881 return (a == x && b == y) || (a == y && b == x);
882 }
883
884 ////////////////////////////////////////////////////////////////////////////////
885 // Utility classes and methods
886
887 /**
888 * The method provides reporting of the same diagnostic only once for copied instructions
889 * (depends on whether it should be reported for all or only for one of the copies)
890 */
891 private void report(
892 @NotNull Diagnostic diagnostic,
893 @NotNull VariableContext ctxt
894 ) {
895 Instruction instruction = ctxt.instruction;
896 if (instruction.getCopies().isEmpty()) {
897 trace.report(diagnostic);
898 return;
899 }
900 Map<Instruction, DiagnosticFactory<?>> previouslyReported = ctxt.reportedDiagnosticMap;
901 previouslyReported.put(instruction, diagnostic.getFactory());
902
903 boolean alreadyReported = false;
904 boolean sameErrorForAllCopies = true;
905 for (Instruction copy : instruction.getCopies()) {
906 DiagnosticFactory<?> previouslyReportedErrorFactory = previouslyReported.get(copy);
907 if (previouslyReportedErrorFactory != null) {
908 alreadyReported = true;
909 }
910
911 if (previouslyReportedErrorFactory != diagnostic.getFactory()) {
912 sameErrorForAllCopies = false;
913 }
914 }
915
916 if (mustBeReportedOnAllCopies(diagnostic.getFactory())) {
917 if (sameErrorForAllCopies) {
918 trace.report(diagnostic);
919 }
920 }
921 else {
922 //only one reporting required
923 if (!alreadyReported) {
924 trace.report(diagnostic);
925 }
926 }
927 }
928
929 private static boolean mustBeReportedOnAllCopies(@NotNull DiagnosticFactory<?> diagnosticFactory) {
930 return diagnosticFactory == UNUSED_VARIABLE
931 || diagnosticFactory == UNUSED_PARAMETER
932 || diagnosticFactory == UNUSED_CHANGED_VALUE;
933 }
934
935
936 private class VariableContext {
937 final Map<Instruction, DiagnosticFactory<?>> reportedDiagnosticMap;
938 final Instruction instruction;
939 final VariableDescriptor variableDescriptor;
940
941 private VariableContext(
942 @NotNull Instruction instruction,
943 @NotNull Map<Instruction, DiagnosticFactory<?>> map
944 ) {
945 this.instruction = instruction;
946 reportedDiagnosticMap = map;
947 variableDescriptor = PseudocodeUtil.extractVariableDescriptorIfAny(instruction, true, trace.getBindingContext());
948 }
949 }
950
951 private class VariableInitContext extends VariableContext {
952 final VariableInitState enterInitState;
953 final VariableInitState exitInitState;
954
955 private VariableInitContext(
956 @NotNull Instruction instruction,
957 @NotNull Map<Instruction, DiagnosticFactory<?>> map,
958 @NotNull Map<VariableDescriptor, VariableInitState> in,
959 @NotNull Map<VariableDescriptor, VariableInitState> out,
960 @NotNull LexicalScopeVariableInfo lexicalScopeVariableInfo
961 ) {
962 super(instruction, map);
963 enterInitState = initialize(variableDescriptor, lexicalScopeVariableInfo, in);
964 exitInitState = initialize(variableDescriptor, lexicalScopeVariableInfo, out);
965 }
966
967 private VariableInitState initialize(
968 VariableDescriptor variableDescriptor,
969 LexicalScopeVariableInfo lexicalScopeVariableInfo,
970 Map<VariableDescriptor, VariableInitState> map
971 ) {
972 if (variableDescriptor == null) return null;
973 VariableInitState state = map.get(variableDescriptor);
974 if (state != null) return state;
975 return PseudocodeVariablesData.getDefaultValueForInitializers(variableDescriptor, instruction, lexicalScopeVariableInfo);
976 }
977 }
978
979 private class VariableUseContext extends VariableContext {
980 final VariableUseState enterUseState;
981 final VariableUseState exitUseState;
982
983
984 private VariableUseContext(
985 @NotNull Instruction instruction,
986 @NotNull Map<Instruction, DiagnosticFactory<?>> map,
987 @NotNull Map<VariableDescriptor, VariableUseState> in,
988 @NotNull Map<VariableDescriptor, VariableUseState> out
989 ) {
990 super(instruction, map);
991 enterUseState = variableDescriptor != null ? in.get(variableDescriptor) : null;
992 exitUseState = variableDescriptor != null ? out.get(variableDescriptor) : null;
993 }
994 }
995
996 //TODO after KT-4621 rewrite to Kotlin
997 public abstract static class InstructionDataAnalyzeStrategy<D> implements Function3<Instruction, D, D, Unit> {
998 @Override
999 public Unit invoke(Instruction instruction, D enterData, D exitData) {
1000 execute(instruction, enterData, exitData);
1001 return Unit.INSTANCE$;
1002 }
1003
1004 public abstract void execute(Instruction instruction, D enterData, D exitData);
1005 }
1006
1007 public abstract static class FunctionVoid1<P> implements Function1<P, Unit> {
1008 @Override
1009 public Unit invoke(P p) {
1010 execute(p);
1011 return Unit.INSTANCE$;
1012 }
1013
1014 public abstract void execute(P p);
1015 }
1016 }