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