/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.checkers;
import com.intellij.psi.
PsiElement;
import com.intellij.psi.tree.
IElementType;
import com.intellij.psi.tree.
TokenSet;
import com.intellij.psi.util.
PsiTreeUtil;
import org.jetbrains.annotations.
NotNull;
import org.jetbrains.kotlin.
KtNodeTypes;
import org.jetbrains.kotlin.descriptors.
CallableDescriptor;
import org.jetbrains.kotlin.descriptors.
DeclarationDescriptor;
import org.jetbrains.kotlin.descriptors.
PropertyDescriptor;
import org.jetbrains.kotlin.descriptors.
VariableDescriptor;
import org.jetbrains.kotlin.diagnostics.
Diagnostic;
import org.jetbrains.kotlin.diagnostics.
DiagnosticFactory;
import org.jetbrains.kotlin.diagnostics.
Errors;
import org.jetbrains.kotlin.lexer.
KtTokens;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.resolve.
BindingContext;
import org.jetbrains.kotlin.resolve.
BindingContextUtils;
import org.jetbrains.kotlin.resolve.calls.callUtil.
CallUtilKt;
import org.jetbrains.kotlin.resolve.calls.model.
ResolvedCall;
import org.jetbrains.kotlin.resolve.calls.tasks.
DynamicCallsKt;
import org.jetbrains.kotlin.types.
ErrorUtils;
import org.jetbrains.kotlin.types.
KotlinType;
import org.jetbrains.kotlin.util.slicedMap.
WritableSlice;
import java.util.
Collection;
import java.util.
HashMap;
import java.util.
Map;
import static org.jetbrains.kotlin.lexer.
KtTokens.*;
import static org.jetbrains.kotlin.resolve.
BindingContext.*;
public class
DebugInfoUtil {
private static final
TokenSet MAY_BE_UNRESOLVED =
TokenSet.
create(
IN_KEYWORD,
NOT_IN);
private static final
TokenSet EXCLUDED =
TokenSet.
create(
COLON,
AS_KEYWORD,
AS_SAFE,
IS_KEYWORD,
NOT_IS,
OROR,
ANDAND,
EQ,
EQEQEQ,
EXCLEQEQEQ,
ELVIS,
EXCLEXCL);
public abstract static class
DebugInfoReporter {
public void
preProcessReference(@
NotNull KtReferenceExpression expression) {
// do nothing
}
public abstract void
reportElementWithErrorType(@
NotNull KtReferenceExpression expression);
public abstract void
reportMissingUnresolved(@
NotNull KtReferenceExpression expression);
public abstract void
reportUnresolvedWithTarget(@
NotNull KtReferenceExpression expression, @
NotNull String target);
public void
reportDynamicCall(@
NotNull KtElement element,
DeclarationDescriptor declarationDescriptor) { }
}
public static void
markDebugAnnotations(
@
NotNull PsiElement root,
@
NotNull BindingContext bindingContext,
@
NotNull DebugInfoReporter debugInfoReporter
) {
Map<
KtReferenceExpression,
DiagnosticFactory<?>>
markedWithErrorElements = new
HashMap<>();
for (
Diagnostic diagnostic :
bindingContext.
getDiagnostics()) {
DiagnosticFactory<?>
factory =
diagnostic.
getFactory();
if (
Errors.
UNRESOLVED_REFERENCE_DIAGNOSTICS.
contains(
diagnostic.
getFactory())) {
markedWithErrorElements.
put((
KtReferenceExpression)
diagnostic.
getPsiElement(),
factory);
}
else if (
factory ==
Errors.
SUPER_IS_NOT_AN_EXPRESSION
||
factory ==
Errors.
SUPER_NOT_AVAILABLE) {
KtSuperExpression superExpression = (
KtSuperExpression)
diagnostic.
getPsiElement();
markedWithErrorElements.
put(
superExpression.
getInstanceReference(),
factory);
}
else if (
factory ==
Errors.
EXPRESSION_EXPECTED_PACKAGE_FOUND) {
markedWithErrorElements.
put((
KtSimpleNameExpression)
diagnostic.
getPsiElement(),
factory);
}
else if (
factory ==
Errors.
UNSUPPORTED) {
for (
KtReferenceExpression reference :
PsiTreeUtil.
findChildrenOfType(
diagnostic.
getPsiElement(),
KtReferenceExpression.class)) {
markedWithErrorElements.
put(
reference,
factory);
}
}
}
root.
acceptChildren(new
KtTreeVisitorVoid() {
@
Override
public void
visitForExpression(@
NotNull KtForExpression expression) {
KtExpression range =
expression.
getLoopRange();
reportIfDynamicCall(
range,
range,
LOOP_RANGE_ITERATOR_RESOLVED_CALL);
reportIfDynamicCall(
range,
range,
LOOP_RANGE_HAS_NEXT_RESOLVED_CALL);
reportIfDynamicCall(
range,
range,
LOOP_RANGE_NEXT_RESOLVED_CALL);
super.visitForExpression(
expression);
}
@
Override
public void
visitDestructuringDeclaration(@
NotNull KtDestructuringDeclaration destructuringDeclaration) {
for (
KtDestructuringDeclarationEntry entry :
destructuringDeclaration.
getEntries()) {
reportIfDynamicCall(
entry,
entry,
COMPONENT_RESOLVED_CALL);
}
super.visitDestructuringDeclaration(
destructuringDeclaration);
}
@
Override
public void
visitProperty(@
NotNull KtProperty property) {
VariableDescriptor descriptor =
bindingContext.
get(
VARIABLE,
property);
if (
descriptor instanceof
PropertyDescriptor &&
property.
getDelegate() != null) {
PropertyDescriptor propertyDescriptor = (
PropertyDescriptor)
descriptor;
reportIfDynamicCall(
property.
getDelegate(),
propertyDescriptor,
PROVIDE_DELEGATE_RESOLVED_CALL);
reportIfDynamicCall(
property.
getDelegate(),
propertyDescriptor.
getGetter(),
DELEGATED_PROPERTY_RESOLVED_CALL);
reportIfDynamicCall(
property.
getDelegate(),
propertyDescriptor.
getSetter(),
DELEGATED_PROPERTY_RESOLVED_CALL);
}
super.visitProperty(
property);
}
@
Override
public void
visitThisExpression(@
NotNull KtThisExpression expression) {
ResolvedCall<? extends
CallableDescriptor>
resolvedCall =
CallUtilKt.
getResolvedCall(
expression,
bindingContext);
if (
resolvedCall != null) {
reportIfDynamic(
expression,
resolvedCall.
getResultingDescriptor(),
debugInfoReporter);
}
super.visitThisExpression(
expression);
}
@
Override
public void
visitReferenceExpression(@
NotNull KtReferenceExpression expression) {
super.visitReferenceExpression(
expression);
if (!
BindingContextUtils.
isExpressionWithValidReference(
expression,
bindingContext)){
return;
}
IElementType referencedNameElementType = null;
if (
expression instanceof
KtSimpleNameExpression) {
KtSimpleNameExpression nameExpression = (
KtSimpleNameExpression)
expression;
IElementType elementType =
expression.
getNode().
getElementType();
if (
elementType ==
KtNodeTypes.
OPERATION_REFERENCE) {
referencedNameElementType =
nameExpression.
getReferencedNameElementType();
if (
EXCLUDED.
contains(
referencedNameElementType)) {
return;
}
}
if (
elementType ==
KtNodeTypes.
LABEL ||
nameExpression.
getReferencedNameElementType() ==
KtTokens.
THIS_KEYWORD) {
return;
}
}
debugInfoReporter.
preProcessReference(
expression);
String target = null;
DeclarationDescriptor declarationDescriptor =
bindingContext.
get(
REFERENCE_TARGET,
expression);
if (
declarationDescriptor != null) {
target =
declarationDescriptor.
toString();
reportIfDynamic(
expression,
declarationDescriptor,
debugInfoReporter);
}
if (
target == null) {
PsiElement labelTarget =
bindingContext.
get(
LABEL_TARGET,
expression);
if (
labelTarget != null) {
target =
labelTarget.
getText();
}
}
if (
target == null) {
Collection<? extends
DeclarationDescriptor>
declarationDescriptors =
bindingContext.
get(
AMBIGUOUS_REFERENCE_TARGET,
expression);
if (
declarationDescriptors != null) {
target = "[" +
declarationDescriptors.
size() + " descriptors]";
}
}
if (
target == null) {
Collection<? extends
PsiElement>
labelTargets =
bindingContext.
get(
AMBIGUOUS_LABEL_TARGET,
expression);
if (
labelTargets != null) {
target = "[" +
labelTargets.
size() + " elements]";
}
}
if (
MAY_BE_UNRESOLVED.
contains(
referencedNameElementType)) {
return;
}
boolean
resolved =
target != null;
boolean
markedWithError =
markedWithErrorElements.
containsKey(
expression);
if (
expression instanceof
KtArrayAccessExpression &&
markedWithErrorElements.
containsKey(((
KtArrayAccessExpression)
expression).
getArrayExpression())) {
// if 'foo' in 'foo[i]' is unresolved it means 'foo[i]' is unresolved (otherwise 'foo[i]' is marked as 'missing unresolved')
markedWithError = true;
}
KotlinType expressionType =
bindingContext.
getType(
expression);
DiagnosticFactory<?>
factory =
markedWithErrorElements.
get(
expression);
if (
declarationDescriptor != null &&
(
ErrorUtils.
isError(
declarationDescriptor) ||
ErrorUtils.
containsErrorType(
expressionType))) {
if (
factory !=
Errors.
EXPRESSION_EXPECTED_PACKAGE_FOUND) {
debugInfoReporter.
reportElementWithErrorType(
expression);
}
}
if (
resolved &&
markedWithError) {
if (
Errors.
UNRESOLVED_REFERENCE_DIAGNOSTICS.
contains(
factory)) {
debugInfoReporter.
reportUnresolvedWithTarget(
expression,
target);
}
}
else if (!
resolved && !
markedWithError) {
debugInfoReporter.
reportMissingUnresolved(
expression);
}
}
private <E extends
KtElement, K, D extends
CallableDescriptor> boolean
reportIfDynamicCall(E
element, K
key,
WritableSlice<K,
ResolvedCall<D>>
slice) {
ResolvedCall<D>
resolvedCall =
bindingContext.
get(
slice,
key);
if (
resolvedCall != null) {
return
reportIfDynamic(
element,
resolvedCall.
getResultingDescriptor(),
debugInfoReporter);
}
return false;
}
});
}
private static boolean
reportIfDynamic(
KtElement element,
DeclarationDescriptor declarationDescriptor,
DebugInfoReporter debugInfoReporter) {
if (
declarationDescriptor != null &&
DynamicCallsKt.
isDynamic(
declarationDescriptor)) {
debugInfoReporter.
reportDynamicCall(
element,
declarationDescriptor);
return true;
}
return false;
}
}