resolve a!! as call

This commit is contained in:
Svetlana Isakova
2013-08-02 14:13:00 +04:00
parent 46cb6b3242
commit aba70e047d
6 changed files with 164 additions and 96 deletions
@@ -17,7 +17,6 @@
package org.jetbrains.jet.lang.types.expressions;
import com.google.common.collect.Multimap;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import com.intellij.psi.tree.IElementType;
import org.jetbrains.annotations.NotNull;
@@ -69,6 +68,7 @@ import static org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue.NO_R
import static org.jetbrains.jet.lang.types.TypeUtils.NO_EXPECTED_TYPE;
import static org.jetbrains.jet.lang.types.TypeUtils.UNKNOWN_EXPECTED_TYPE;
import static org.jetbrains.jet.lang.types.TypeUtils.noExpectedType;
import static org.jetbrains.jet.lang.types.expressions.ControlStructureTypingUtils.*;
import static org.jetbrains.jet.lang.types.expressions.ExpressionTypingUtils.*;
@SuppressWarnings("SuspiciousMethodCalls")
@@ -723,8 +723,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
@Override
public JetTypeInfo visitQualifiedExpression(JetQualifiedExpression expression, ExpressionTypingContext context) {
CallExpressionResolver callExpressionResolver = context.expressionTypingServices.getCallExpressionResolver();
return callExpressionResolver
.getQualifiedExpressionTypeInfo(expression, context);
return callExpressionResolver.getQualifiedExpressionTypeInfo(expression, context);
}
@Override
@@ -827,27 +826,25 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
JetSimpleNameExpression operationSign = expression.getOperationReference();
assert operationSign.getReferencedNameElementType() == JetTokens.EXCLEXCL;
JetType expectedType;
if (!noExpectedType(context.expectedType)) {
expectedType = TypeUtils.makeNullable(context.expectedType);
Call call = createCallForSpecialConstruction(expression, Collections.singletonList(baseExpression));
ResolvedCall<FunctionDescriptor> resolvedCall = resolveSpecialConstructionAsCall(
call, "ExclExcl", Collections.singletonList("baseExpr"), Collections.singletonList(true), context, null);
resolvedCall.getResultingDescriptor();
JetTypeInfo baseTypeInfo = BindingContextUtils.getRecordedTypeInfo(baseExpression, context.trace.getBindingContext());
assert baseTypeInfo != null : "Base expression was not processed: " + expression;
JetType baseType = baseTypeInfo.getType();
if (baseType == null) {
return baseTypeInfo;
}
DataFlowInfo dataFlowInfo = baseTypeInfo.getDataFlowInfo();
if (isKnownToBeNotNull(baseExpression, context) && !ErrorUtils.isErrorType(baseType)) {
context.trace.report(UNNECESSARY_NOT_NULL_ASSERTION.on(operationSign, baseType));
}
else {
expectedType = NO_EXPECTED_TYPE;
}
JetTypeInfo typeInfo = facade.getTypeInfo(baseExpression, context.replaceExpectedType(expectedType));
JetType type = typeInfo.getType();
if (type == null) {
return typeInfo;
}
DataFlowInfo dataFlowInfo = typeInfo.getDataFlowInfo();
if (isKnownToBeNotNull(baseExpression, context) && !ErrorUtils.isErrorType(type)) {
context.trace.report(UNNECESSARY_NOT_NULL_ASSERTION.on(operationSign, type));
}
else {
DataFlowValue value = DataFlowValueFactory.INSTANCE.createDataFlowValue(baseExpression, type, context.trace.getBindingContext());
DataFlowValue value = DataFlowValueFactory.INSTANCE.createDataFlowValue(baseExpression, baseType, context.trace.getBindingContext());
dataFlowInfo = dataFlowInfo.disequate(value, DataFlowValue.NULL);
}
return JetTypeInfo.create(TypeUtils.makeNotNullable(type), dataFlowInfo);
return JetTypeInfo.create(TypeUtils.makeNotNullable(baseType), dataFlowInfo);
}
private JetTypeInfo visitLabeledExpression(@NotNull JetUnaryExpression expression, @NotNull ExpressionTypingContext context,
@@ -17,6 +17,7 @@
package org.jetbrains.jet.lang.types.expressions;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
@@ -29,6 +30,7 @@ import org.jetbrains.jet.lang.descriptors.impl.ValueParameterDescriptorImpl;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContextUtils;
import org.jetbrains.jet.lang.resolve.BindingTrace;
import org.jetbrains.jet.lang.resolve.calls.CallResolver;
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystem;
import org.jetbrains.jet.lang.resolve.calls.inference.InferenceErrorData;
@@ -43,10 +45,12 @@ import org.jetbrains.jet.lang.resolve.name.Name;
import org.jetbrains.jet.lang.resolve.scopes.JetScope;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue;
import org.jetbrains.jet.lang.types.*;
import org.jetbrains.jet.lexer.JetTokens;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static org.jetbrains.jet.lang.resolve.BindingContext.CALL;
import static org.jetbrains.jet.lang.resolve.BindingContext.RESOLVED_CALL;
@@ -55,52 +59,70 @@ public class ControlStructureTypingUtils {
private ControlStructureTypingUtils() {
}
/*package*/ static ResolvedCall<FunctionDescriptor> resolveIfAsCall(
@NotNull Call callForIf,
/*package*/ static ResolvedCall<FunctionDescriptor> resolveSpecialConstructionAsCall(
@NotNull Call call,
@NotNull String constructionName,
@NotNull List<String> argumentNames,
@NotNull List<Boolean> isArgumentNullable,
@NotNull ExpressionTypingContext context,
@NotNull DataFlowInfo thenInfo,
@NotNull DataFlowInfo elseInfo
@Nullable MutableDataFlowInfoForArguments dataFlowInfoForArguments
) {
List<AnnotationDescriptor> noAnnotations = Collections.emptyList();
Name specialFunctionName = Name.identifierNoValidate("<SPECIAL-FUNCTION-FOR-IF-RESOLVE>");
SimpleFunctionDescriptorImpl function = createFunctionDescriptorForSpecialConstruction(
"<SPECIAL-FUNCTION-FOR-" + constructionName.toUpperCase() + "-RESOLVE>", argumentNames, isArgumentNullable);
JetReferenceExpression reference = JetPsiFactory.createSimpleName(
context.expressionTypingServices.getProject(), "fake" + constructionName + "Call");
TracingStrategy tracing = createTracingForSpecialConstruction(call, constructionName);
ResolutionCandidate<CallableDescriptor> resolutionCandidate = ResolutionCandidate.<CallableDescriptor>create(function, null);
CallResolver callResolver = context.expressionTypingServices.getCallResolver();
OverloadResolutionResults<FunctionDescriptor> results = callResolver.resolveCallWithKnownCandidate(
call, tracing, reference, context, resolutionCandidate, dataFlowInfoForArguments);
assert results.isSingleResult() : "Not single result after resolving one known candidate";
return results.getResultingCall();
}
SimpleFunctionDescriptorImpl ifFunction = new SimpleFunctionDescriptorImpl(
private static SimpleFunctionDescriptorImpl createFunctionDescriptorForSpecialConstruction(
@NotNull String name,
@NotNull List<String> argumentNames,
@NotNull List<Boolean> isArgumentNullable
) {
assert argumentNames.size() == isArgumentNullable.size();
List<AnnotationDescriptor> noAnnotations = Collections.emptyList();
Name specialFunctionName = Name.identifierNoValidate(name);
SimpleFunctionDescriptorImpl function = new SimpleFunctionDescriptorImpl(
ErrorUtils.getErrorModule(),//todo hack to avoid returning true in 'isError(DeclarationDescriptor)'
noAnnotations, specialFunctionName, CallableMemberDescriptor.Kind.DECLARATION);
TypeParameterDescriptor typeParameter = TypeParameterDescriptorImpl.createWithDefaultBound(
ifFunction, noAnnotations, false, Variance.INVARIANT, Name.identifier("T"), 0);
function, noAnnotations, false, Variance.INVARIANT, Name.identifier("T"), 0);
JetType type = new JetTypeImpl(typeParameter.getTypeConstructor(), JetScope.EMPTY);
JetType nullableType = new JetTypeImpl(
noAnnotations, typeParameter.getTypeConstructor(), true, Collections.<TypeProjection>emptyList(), JetScope.EMPTY);
ValueParameterDescriptorImpl thenValueParameter = new ValueParameterDescriptorImpl(
ifFunction, 0, noAnnotations, Name.identifier("thenBranch"), type, false, null);
ValueParameterDescriptorImpl elseValueParameter = new ValueParameterDescriptorImpl(
ifFunction, 1, noAnnotations, Name.identifier("elseBranch"), type, false, null);
ifFunction.initialize(
List<ValueParameterDescriptor> valueParameters = Lists.newArrayList();
for (int i = 0; i < argumentNames.size(); i++) {
JetType argumentType = isArgumentNullable.get(i) ? nullableType : type;
ValueParameterDescriptorImpl valueParameter = new ValueParameterDescriptorImpl(
function, i, noAnnotations, Name.identifier(argumentNames.get(i)), argumentType, false, null);
valueParameters.add(valueParameter);
}
function.initialize(
null,
ReceiverParameterDescriptor.NO_RECEIVER_PARAMETER,
Lists.newArrayList(typeParameter),
Lists.<ValueParameterDescriptor>newArrayList(thenValueParameter, elseValueParameter),
valueParameters,
type,
Modality.FINAL,
Visibilities.PUBLIC,
/*isInline = */ false
);
JetReferenceExpression ifReference = JetPsiFactory.createSimpleName(context.expressionTypingServices.getProject(), "fakeIfCall");
TracingStrategy tracingForIf = createTracingForIf(callForIf);
MutableDataFlowInfoForArguments dataFlowInfoForArguments = createDataFlowInfoForArgumentsForCall(callForIf, thenInfo, elseInfo);
ResolutionCandidate<CallableDescriptor> resolutionCandidate = ResolutionCandidate.<CallableDescriptor>create(ifFunction, null);
OverloadResolutionResults<FunctionDescriptor>
results = context.expressionTypingServices.getCallResolver().resolveCallWithKnownCandidate(
callForIf, tracingForIf, ifReference, context, resolutionCandidate, dataFlowInfoForArguments);
assert results.isSingleResult() : "Not single result after resolving one known candidate";
return results.getResultingCall();
return function;
}
private static MutableDataFlowInfoForArguments createDataFlowInfoForArgumentsForCall(
final Call callForIf, final DataFlowInfo thenInfo, final DataFlowInfo elseInfo
/*package*/ static MutableDataFlowInfoForArguments createDataFlowInfoForArgumentsForCall(
final Map<ValueArgument, DataFlowInfo> dataFlowInfoForArgumentsMap
) {
return new MutableDataFlowInfoForArguments() {
private DataFlowInfo initialDataFlowInfo;
@@ -117,13 +139,7 @@ public class ControlStructureTypingUtils {
@NotNull
@Override
public DataFlowInfo getInfo(@NotNull ValueArgument valueArgument) {
if (valueArgument == callForIf.getValueArguments().get(0)) {
return thenInfo;
}
if (valueArgument == callForIf.getValueArguments().get(1)) {
return elseInfo;
}
throw new IllegalArgumentException();
return dataFlowInfoForArgumentsMap.get(valueArgument);
}
@NotNull
@@ -134,19 +150,30 @@ public class ControlStructureTypingUtils {
};
}
/*package*/ static Call createCallForIf(
final JetIfExpression ifExpression,
@NotNull JetExpression thenBranch,
@NotNull JetExpression elseBranch
public static MutableDataFlowInfoForArguments createDataFlowInfoForArgumentsForIfCall(
@NotNull Call callForIf,
@NotNull DataFlowInfo thenInfo,
@NotNull DataFlowInfo elseInfo
) {
final List<ValueArgument> valueArguments = Lists.newArrayList(
CallMaker.makeValueArgument(thenBranch, thenBranch),
CallMaker.makeValueArgument(elseBranch, elseBranch));
Map<ValueArgument, DataFlowInfo> dataFlowInfoForArgumentsMap = Maps.newHashMap();
dataFlowInfoForArgumentsMap.put(callForIf.getValueArguments().get(0), thenInfo);
dataFlowInfoForArgumentsMap.put(callForIf.getValueArguments().get(1), elseInfo);
return createDataFlowInfoForArgumentsForCall(dataFlowInfoForArgumentsMap);
}
/*package*/ static Call createCallForSpecialConstruction(
@NotNull final JetExpression expression,
@NotNull List<JetExpression> arguments
) {
final List<ValueArgument> valueArguments = Lists.newArrayList();
for (JetExpression argument : arguments) {
valueArguments.add(CallMaker.makeValueArgument(argument, argument));
}
return new Call() {
@Nullable
@Override
public ASTNode getCallOperationNode() {
return ifExpression.getNode();
return expression.getNode();
}
@NotNull
@@ -164,7 +191,7 @@ public class ControlStructureTypingUtils {
@Nullable
@Override
public JetExpression getCalleeExpression() {
return ifExpression;
return expression;
}
@Nullable
@@ -200,7 +227,7 @@ public class ControlStructureTypingUtils {
@NotNull
@Override
public PsiElement getCallElement() {
return ifExpression;
return expression;
}
@NotNull
@@ -211,7 +238,10 @@ public class ControlStructureTypingUtils {
};
}
/*package*/ static TracingStrategy createTracingForIf(final @NotNull Call callForIf) {
/*package*/ static TracingStrategy createTracingForSpecialConstruction(
final @NotNull Call call,
final @NotNull String constructionName
) {
class CheckTypeContext {
public BindingTrace trace;
public JetType expectedType;
@@ -247,6 +277,16 @@ public class ControlStructureTypingUtils {
return null;
}
@Override
public Void visitPostfixExpression(JetPostfixExpression expression, CheckTypeContext c) {
if (expression.getOperationReference().getReferencedNameElementType() == JetTokens.EXCLEXCL) {
checkExpressionType(expression.getBaseExpression(),
new CheckTypeContext(c.trace, TypeUtils.makeNullable(c.expectedType)));
return null;
}
return super.visitPostfixExpression(expression, c);
}
@Override
public Void visitExpression(JetExpression expression, CheckTypeContext c) {
JetTypeInfo typeInfo = BindingContextUtils.getRecordedTypeInfo(expression, c.trace.getBindingContext());
@@ -257,7 +297,7 @@ public class ControlStructureTypingUtils {
}
};
return new ThrowingOnErrorTracingStrategy("resolve 'if' as a call") {
return new ThrowingOnErrorTracingStrategy("resolve " + constructionName + " as a call") {
@Override
public <D extends CallableDescriptor> void bindReference(
@NotNull BindingTrace trace, @NotNull ResolvedCallWithTrace<D> resolvedCall
@@ -269,8 +309,8 @@ public class ControlStructureTypingUtils {
public <D extends CallableDescriptor> void bindResolvedCall(
@NotNull BindingTrace trace, @NotNull ResolvedCallWithTrace<D> resolvedCall
) {
trace.record(RESOLVED_CALL, callForIf.getCalleeExpression(), resolvedCall);
trace.record(CALL, callForIf.getCalleeExpression(), callForIf);
trace.record(RESOLVED_CALL, call.getCalleeExpression(), resolvedCall);
trace.record(CALL, call.getCalleeExpression(), call);
}
@@ -285,7 +325,7 @@ public class ControlStructureTypingUtils {
return;
}
if (constraintSystem.hasOnlyExpectedTypeMismatch()) {
JetExpression expression = callForIf.getCalleeExpression();
JetExpression expression = call.getCalleeExpression();
if (expression != null) {
expression.accept(checkTypeVisitor, new CheckTypeContext(trace, data.expectedType));
}
@@ -34,6 +34,7 @@ import org.jetbrains.jet.lang.resolve.BindingContextUtils;
import org.jetbrains.jet.lang.resolve.DescriptorResolver;
import org.jetbrains.jet.lang.resolve.DescriptorUtils;
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
import org.jetbrains.jet.lang.resolve.calls.model.MutableDataFlowInfoForArguments;
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
import org.jetbrains.jet.lang.resolve.calls.results.OverloadResolutionResults;
import org.jetbrains.jet.lang.resolve.name.Name;
@@ -47,15 +48,13 @@ import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
import org.jetbrains.jet.util.slicedmap.WritableSlice;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.*;
import static org.jetbrains.jet.lang.diagnostics.Errors.*;
import static org.jetbrains.jet.lang.resolve.BindingContext.*;
import static org.jetbrains.jet.lang.types.TypeUtils.UNKNOWN_EXPECTED_TYPE;
import static org.jetbrains.jet.lang.types.TypeUtils.noExpectedType;
import static org.jetbrains.jet.lang.types.expressions.ControlStructureTypingUtils.*;
import static org.jetbrains.jet.lang.types.expressions.ExpressionTypingUtils.*;
public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
@@ -119,8 +118,13 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
JetTypeInfo elseTypeInfo;
if (contextWithExpectedType.expectedType == UNKNOWN_EXPECTED_TYPE) {
Call callForIf = ControlStructureTypingUtils.createCallForIf(ifExpression, thenBranch, elseBranch);
ControlStructureTypingUtils.resolveIfAsCall(callForIf, contextWithExpectedType, thenInfo, elseInfo);
Call callForIf = createCallForSpecialConstruction(ifExpression, Lists.newArrayList(thenBranch, elseBranch));
MutableDataFlowInfoForArguments dataFlowInfoForArguments =
createDataFlowInfoForArgumentsForIfCall(callForIf, thenInfo, elseInfo);
resolveSpecialConstructionAsCall(
callForIf, "If", Lists.newArrayList("thenBranch", "elseBranch"),
Lists.newArrayList(false, false),
contextWithExpectedType, dataFlowInfoForArguments);
thenTypeInfo = BindingContextUtils.getRecordedTypeInfo(thenBranch, context.trace.getBindingContext());
elseTypeInfo = BindingContextUtils.getRecordedTypeInfo(elseBranch, context.trace.getBindingContext());
@@ -0,0 +1,28 @@
package a
trait A
fun <T>id(t: T): T = t
fun doList(l: List<Int>) = l
fun doInt(i: Int) = i
fun <T> strangeNullableList(<!UNUSED_PARAMETER!>f<!>: (T) -> Unit): List<T>? = throw Exception()
fun <T: A> emptyNullableListOfA(): List<T>? = null
//-------------------------------
fun testExclExcl() {
doList(<!TYPE_INFERENCE_UPPER_BOUND_VIOLATED!>emptyNullableListOfA<!>()!!) //should be an error here
val <!UNUSED_VARIABLE!>l<!>: List<Int> = id(<!TYPE_INFERENCE_UPPER_BOUND_VIOLATED!>emptyNullableListOfA<!>()!!)
doList(strangeNullableList { doInt(it) }!!) //lambda should be analyzed (at completion phase)
}
fun testDataFlowInfoAfterExclExcl(a: Int?) {
doInt(a!!)
a + 1
}
fun testUnnecessaryExclExcl(a: Int) {
doInt(a<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>) //should be warning
}
@@ -1258,7 +1258,6 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage
}
@TestMetadata("compiler/testData/diagnostics/tests/controlStructures")
@InnerTestClasses({ControlStructures.If.class})
public static class ControlStructures extends AbstractDiagnosticsTestWithEagerResolve {
public void testAllFilesPresentInControlStructures() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/diagnostics/tests/controlStructures"), Pattern.compile("^(.+)\\.kt$"), true);
@@ -1324,25 +1323,6 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage
doTest("compiler/testData/diagnostics/tests/controlStructures/when.kt234.kt973.kt");
}
@TestMetadata("compiler/testData/diagnostics/tests/controlStructures/if")
public static class If extends AbstractDiagnosticsTestWithEagerResolve {
public void testAllFilesPresentInIf() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/diagnostics/tests/controlStructures/if"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("reportTypeMismatchDeeplyOnBranches.kt")
public void testReportTypeMismatchDeeplyOnBranches() throws Exception {
doTest("compiler/testData/diagnostics/tests/controlStructures/if/reportTypeMismatchDeeplyOnBranches.kt");
}
}
public static Test innerSuite() {
TestSuite suite = new TestSuite("ControlStructures");
suite.addTestSuite(ControlStructures.class);
suite.addTestSuite(If.class);
return suite;
}
}
@TestMetadata("compiler/testData/diagnostics/tests/dataClasses")
@@ -4738,7 +4718,7 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage
}
@TestMetadata("compiler/testData/diagnostics/tests/resolve")
@InnerTestClasses({Resolve.Invoke.class})
@InnerTestClasses({Resolve.Invoke.class, Resolve.SpecialConstructions.class})
public static class Resolve extends AbstractDiagnosticsTestWithEagerResolve {
public void testAllFilesPresentInResolve() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/diagnostics/tests/resolve"), Pattern.compile("^(.+)\\.kt$"), true);
@@ -4807,10 +4787,29 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage
}
@TestMetadata("compiler/testData/diagnostics/tests/resolve/specialConstructions")
public static class SpecialConstructions extends AbstractDiagnosticsTestWithEagerResolve {
public void testAllFilesPresentInSpecialConstructions() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/diagnostics/tests/resolve/specialConstructions"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("exclExclAsCall.kt")
public void testExclExclAsCall() throws Exception {
doTest("compiler/testData/diagnostics/tests/resolve/specialConstructions/exclExclAsCall.kt");
}
@TestMetadata("reportTypeMismatchDeeplyOnBranches.kt")
public void testReportTypeMismatchDeeplyOnBranches() throws Exception {
doTest("compiler/testData/diagnostics/tests/resolve/specialConstructions/reportTypeMismatchDeeplyOnBranches.kt");
}
}
public static Test innerSuite() {
TestSuite suite = new TestSuite("Resolve");
suite.addTestSuite(Resolve.class);
suite.addTestSuite(Invoke.class);
suite.addTestSuite(SpecialConstructions.class);
return suite;
}
}
@@ -5378,7 +5377,7 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage
suite.addTestSuite(Cast.class);
suite.addTestSuite(CheckArguments.class);
suite.addTestSuite(ControlFlowAnalysis.class);
suite.addTest(ControlStructures.innerSuite());
suite.addTestSuite(ControlStructures.class);
suite.addTestSuite(DataClasses.class);
suite.addTest(DataFlow.innerSuite());
suite.addTestSuite(DataFlowInfoTraversal.class);