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