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