org.sosy_lab.cpachecker.util.VariableClassificationBuilder.java Source code

Java tutorial

Introduction

Here is the source code for org.sosy_lab.cpachecker.util.VariableClassificationBuilder.java

Source

/*
 *  CPAchecker is a tool for configurable software verification.
 *  This file is part of CPAchecker.
 *
 *  Copyright (C) 2007-2014  Dirk Beyer
 *  All rights reserved.
 *
 *  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.
 *
 *
 *  CPAchecker web page:
 *    http://cpachecker.sosy-lab.org
 */
package org.sosy_lab.cpachecker.util;

import static com.google.common.base.Preconditions.*;
import static org.sosy_lab.cpachecker.util.CFAUtils.leavingEdges;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.Writer;
import java.math.BigInteger;
import java.util.ArrayDeque;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.TreeSet;
import java.util.logging.Level;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import org.sosy_lab.common.Pair;
import org.sosy_lab.common.configuration.Configuration;
import org.sosy_lab.common.configuration.FileOption;
import org.sosy_lab.common.configuration.InvalidConfigurationException;
import org.sosy_lab.common.configuration.Option;
import org.sosy_lab.common.configuration.Options;
import org.sosy_lab.common.io.Files;
import org.sosy_lab.common.io.Path;
import org.sosy_lab.common.io.Paths;
import org.sosy_lab.common.log.LogManager;
import org.sosy_lab.cpachecker.cfa.CFA;
import org.sosy_lab.cpachecker.cfa.Language;
import org.sosy_lab.cpachecker.cfa.ast.AVariableDeclaration;
import org.sosy_lab.cpachecker.cfa.ast.AReturnStatement;
import org.sosy_lab.cpachecker.cfa.ast.c.CArraySubscriptExpression;
import org.sosy_lab.cpachecker.cfa.ast.c.CAssignment;
import org.sosy_lab.cpachecker.cfa.ast.c.CBinaryExpression;
import org.sosy_lab.cpachecker.cfa.ast.c.CCastExpression;
import org.sosy_lab.cpachecker.cfa.ast.c.CCharLiteralExpression;
import org.sosy_lab.cpachecker.cfa.ast.c.CComplexCastExpression;
import org.sosy_lab.cpachecker.cfa.ast.c.CDeclaration;
import org.sosy_lab.cpachecker.cfa.ast.c.CExpression;
import org.sosy_lab.cpachecker.cfa.ast.c.CExpressionAssignmentStatement;
import org.sosy_lab.cpachecker.cfa.ast.c.CExpressionVisitor;
import org.sosy_lab.cpachecker.cfa.ast.c.CFieldReference;
import org.sosy_lab.cpachecker.cfa.ast.c.CFloatLiteralExpression;
import org.sosy_lab.cpachecker.cfa.ast.c.CFunctionCall;
import org.sosy_lab.cpachecker.cfa.ast.c.CFunctionCallAssignmentStatement;
import org.sosy_lab.cpachecker.cfa.ast.c.CFunctionCallExpression;
import org.sosy_lab.cpachecker.cfa.ast.c.CFunctionCallStatement;
import org.sosy_lab.cpachecker.cfa.ast.c.CIdExpression;
import org.sosy_lab.cpachecker.cfa.ast.c.CImaginaryLiteralExpression;
import org.sosy_lab.cpachecker.cfa.ast.c.CInitializer;
import org.sosy_lab.cpachecker.cfa.ast.c.CInitializerExpression;
import org.sosy_lab.cpachecker.cfa.ast.c.CInitializers;
import org.sosy_lab.cpachecker.cfa.ast.c.CIntegerLiteralExpression;
import org.sosy_lab.cpachecker.cfa.ast.c.CLeftHandSide;
import org.sosy_lab.cpachecker.cfa.ast.c.CParameterDeclaration;
import org.sosy_lab.cpachecker.cfa.ast.c.CPointerExpression;
import org.sosy_lab.cpachecker.cfa.ast.c.CRightHandSide;
import org.sosy_lab.cpachecker.cfa.ast.c.CRightHandSideVisitor;
import org.sosy_lab.cpachecker.cfa.ast.c.CSimpleDeclaration;
import org.sosy_lab.cpachecker.cfa.ast.c.CStatement;
import org.sosy_lab.cpachecker.cfa.ast.c.CStringLiteralExpression;
import org.sosy_lab.cpachecker.cfa.ast.c.CTypeIdExpression;
import org.sosy_lab.cpachecker.cfa.ast.c.CUnaryExpression;
import org.sosy_lab.cpachecker.cfa.ast.c.CUnaryExpression.UnaryOperator;
import org.sosy_lab.cpachecker.cfa.ast.c.CVariableDeclaration;
import org.sosy_lab.cpachecker.cfa.ast.c.DefaultCExpressionVisitor;
import org.sosy_lab.cpachecker.cfa.model.CFAEdge;
import org.sosy_lab.cpachecker.cfa.model.CFANode;
import org.sosy_lab.cpachecker.cfa.model.FunctionEntryNode;
import org.sosy_lab.cpachecker.cfa.model.MultiEdge;
import org.sosy_lab.cpachecker.cfa.model.c.CAssumeEdge;
import org.sosy_lab.cpachecker.cfa.model.c.CDeclarationEdge;
import org.sosy_lab.cpachecker.cfa.model.c.CFunctionCallEdge;
import org.sosy_lab.cpachecker.cfa.model.c.CFunctionReturnEdge;
import org.sosy_lab.cpachecker.cfa.model.c.CFunctionSummaryEdge;
import org.sosy_lab.cpachecker.cfa.model.c.CReturnStatementEdge;
import org.sosy_lab.cpachecker.cfa.model.c.CStatementEdge;
import org.sosy_lab.cpachecker.cfa.types.c.CCompositeType;
import org.sosy_lab.cpachecker.cfa.types.c.CPointerType;
import org.sosy_lab.cpachecker.cfa.types.c.CSimpleType;
import org.sosy_lab.cpachecker.cfa.types.c.CType;
import org.sosy_lab.cpachecker.exceptions.UnrecognizedCCodeException;
import org.sosy_lab.cpachecker.util.VariableClassification.Partition;

import com.google.common.base.Joiner;
import com.google.common.base.Optional;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;

@Options(prefix = "cfa.variableClassification")
public class VariableClassificationBuilder {

    @Option(secure = true, name = "logfile", description = "Dump variable classification to a file.")
    @FileOption(FileOption.Type.OUTPUT_FILE)
    private Path dumpfile = Paths.get("VariableClassification.log");

    @Option(secure = true, description = "Dump variable type mapping to a file.")
    @FileOption(FileOption.Type.OUTPUT_FILE)
    private Path typeMapFile = Paths.get("VariableTypeMapping.txt");

    @Option(secure = true, description = "Dump domain type statistics to a CSV file.")
    @FileOption(FileOption.Type.OUTPUT_FILE)
    private Path domainTypeStatisticsFile = null;

    @Option(secure = true, description = "Print some information about the variable classification.")
    private boolean printStatsOnStartup = false;

    /**
     * Use {@link FunctionEntryNode#getReturnVariable()} and
     * {@link AReturnStatement#asAssignment()} instead.
     */
    @Deprecated
    public static final String FUNCTION_RETURN_VARIABLE = "__retval__";

    private static final String SCOPE_SEPARATOR = "::";

    /** normally a boolean value would be 0 or 1,
     * however there are cases, where the values are only 0 and 1,
     * but the variable is not boolean at all: "int x; if(x!=0 && x!= 1){}".
     * so we allow only 0 as boolean value, and not 1. */
    private boolean allowOneAsBooleanValue = false;

    private final Set<String> allVars = new HashSet<>();

    private final Set<String> nonIntBoolVars = new HashSet<>();
    private final Set<String> nonIntEqVars = new HashSet<>();
    private final Set<String> nonIntAddVars = new HashSet<>();

    private final Dependencies dependencies = new Dependencies();

    /** These sets contain all variables even ones of array, pointer or structure types.
     *  Such variables cannot be classified even as Int, so they are only kept in these sets in order
     *  not to break the classification of Int variables.*/
    // Initially contains variables used in assumes and assigned to pointer dereferences,
    // then all essential variables (by propagation)
    private final Set<String> relevantVariables = new HashSet<>();
    private final Set<String> addressedVariables = new HashSet<>();

    // Variables and fields used in the right hand side
    private final Multimap<VariableOrField, VariableOrField> assignments = LinkedHashMultimap.create();

    /** Fields information doesn't take any aliasing information into account,
     *  fields are considered per type, not per composite instance */
    // Initially contains fields used in assumes and assigned to pointer dereferences,
    // then all essential fields (by propagation)
    private final Multimap<CCompositeType, String> relevantFields = LinkedHashMultimap.create();

    private final CollectingLHSVisitor collectingLHSVisitor = new CollectingLHSVisitor();

    private final LogManager logger;

    public VariableClassificationBuilder(Configuration config, LogManager pLogger)
            throws InvalidConfigurationException {
        logger = checkNotNull(pLogger);
        config.inject(this);
    }

    /** This function does the whole work:
     * creating all maps, collecting vars, solving dependencies.
     * The function runs only once, after that it does nothing. */
    public VariableClassification build(CFA cfa) throws UnrecognizedCCodeException {
        checkArgument(cfa.getLanguage() == Language.C, "VariableClassification currently only supports C");

        // fill maps
        collectVars(cfa);

        // if a value is not boolean, all dependent vars are not boolean and viceversa
        dependencies.solve(nonIntBoolVars);
        dependencies.solve(nonIntEqVars);
        dependencies.solve(nonIntAddVars);

        // Now build the opposites of each non-x-vars-collection.
        // This is responsible for the hierarchy of the variables.
        final Set<String> intBoolVars = new HashSet<>();
        final Set<String> intEqualVars = new HashSet<>();
        final Set<String> intAddVars = new HashSet<>();
        final Set<Partition> intBoolPartitions = new HashSet<>();
        final Set<Partition> intEqualPartitions = new HashSet<>();
        final Set<Partition> intAddPartitions = new HashSet<>();
        for (final String var : allVars) {
            // we have this hierarchy of classes for variables:
            //        IntBool < IntEqBool < IntAddEqBool < AllInt
            // we define and build:
            //        IntBool = IntBool
            //        IntEq   = IntEqBool - IntBool
            //        IntAdd  = IntAddEqBool - IntEqBool
            //        Other   = IntAll - IntAddEqBool

            if (!nonIntBoolVars.contains(var)) {
                intBoolVars.add(var);
                intBoolPartitions.add(dependencies.getPartitionForVar(var));

            } else if (!nonIntEqVars.contains(var)) {
                intEqualVars.add(var);
                intEqualPartitions.add(dependencies.getPartitionForVar(var));

            } else if (!nonIntAddVars.contains(var)) {
                intAddVars.add(var);
                intAddPartitions.add(dependencies.getPartitionForVar(var));
            }
        }

        propagateRelevancy();

        // add last vars to dependencies,
        // this allows to get partitions for all vars,
        // otherwise only dependent vars are in the partitions
        for (String var : allVars) {
            dependencies.addVar(var);
        }

        boolean hasRelevantNonIntAddVars = !Sets.intersection(relevantVariables, nonIntAddVars).isEmpty();

        VariableClassification result = new VariableClassification(hasRelevantNonIntAddVars, intBoolVars,
                intEqualVars, intAddVars, relevantVariables, addressedVariables, relevantFields,
                dependencies.partitions, intBoolPartitions, intEqualPartitions, intAddPartitions,
                dependencies.edgeToPartition);

        if (printStatsOnStartup) {
            printStats(result);
        }

        if (dumpfile != null) { // option -noout
            try (Writer w = Files.openOutputFile(dumpfile)) {
                w.append("IntBool\n\n");
                w.append(intBoolVars.toString());
                w.append("\n\nIntEq\n\n");
                w.append(intEqualVars.toString());
                w.append("\n\nIntAdd\n\n");
                w.append(intAddVars.toString());
                w.append("\n\nALL\n\n");
                w.append(allVars.toString());
            } catch (IOException e) {
                logger.logUserException(Level.WARNING, e, "Could not write variable classification to file");
            }
        }

        if (typeMapFile != null) {
            dumpVariableTypeMapping(typeMapFile, result);
        }

        if (domainTypeStatisticsFile != null) {
            dumpDomainTypeStatistics(domainTypeStatisticsFile, result);
        }

        return result;
    }

    private void dumpDomainTypeStatistics(Path pDomainTypeStatisticsFile, VariableClassification vc) {
        try (Writer w = Files.openOutputFile(pDomainTypeStatisticsFile)) {
            try (PrintWriter p = new PrintWriter(w)) {
                Object[][] statMapping = { { "intBoolVars", vc.getIntBoolVars().size() },
                        { "intEqualVars", vc.getIntEqualVars().size() },
                        { "intAddVars", vc.getIntAddVars().size() }, { "allVars", allVars.size() },
                        { "intBoolVarsRelevant", countNumberOfRelevantVars(vc.getIntBoolVars()) },
                        { "intEqualVarsRelevant", countNumberOfRelevantVars(vc.getIntEqualVars()) },
                        { "intAddVarsRelevant", countNumberOfRelevantVars(vc.getIntAddVars()) },
                        { "allVarsRelevant", countNumberOfRelevantVars(allVars) } };
                // Write header
                for (int col = 0; col < statMapping.length; col++) {
                    p.print(statMapping[col][0]);
                    if (col != statMapping.length - 1) {
                        p.print("\t");
                    }
                }
                p.print("\n");
                // Write data
                for (int col = 0; col < statMapping.length; col++) {
                    p.print(statMapping[col][1]);
                    if (col != statMapping.length - 1) {
                        p.print("\t");
                    }
                }
                p.print("\n");
            }
        } catch (IOException e) {
            logger.logUserException(Level.WARNING, e, "Could not write variable classification statistics to file");
        }
    }

    private void dumpVariableTypeMapping(Path target, VariableClassification vc) {
        try (Writer w = Files.openOutputFile(target)) {
            for (String var : allVars) {
                byte type = 0;
                if (vc.getIntBoolVars().contains(var)) {
                    type += 1 + 2 + 4; // IntBool is subset of IntEqualBool and IntAddEqBool
                } else if (vc.getIntEqualVars().contains(var)) {
                    type += 2 + 4; // IntEqual is subset of IntAddEqBool
                } else if (vc.getIntAddVars().contains(var)) {
                    type += 4;
                }
                w.append(String.format("%s\t%d%n", var, type));
            }
        } catch (IOException e) {
            logger.logUserException(Level.WARNING, e, "Could not write variable type mapping to file");
        }
    }

    private void printStats(VariableClassification vc) {
        int numOfBooleans = 0;
        for (Partition p : vc.getIntEqualPartitions()) {
            numOfBooleans += p.getVars().size();
        }
        assert numOfBooleans == vc.getIntBoolVars().size();

        int numOfIntEquals = 0;
        for (Partition p : vc.getIntEqualPartitions()) {
            numOfIntEquals += p.getVars().size();
        }
        assert numOfIntEquals == vc.getIntEqualVars().size();

        int numOfIntAdds = 0;
        for (Partition p : vc.getIntAddPartitions()) {
            numOfIntAdds += p.getVars().size();
        }
        assert numOfIntAdds == vc.getIntAddVars().size();

        final String prefix = "\nVC ";
        StringBuilder str = new StringBuilder("VariableClassification Statistics\n");
        Joiner.on(prefix).appendTo(str,
                new String[] { "---------------------------------", "number of boolean vars:  " + numOfBooleans,
                        "number of intEq vars:    " + numOfIntEquals, "number of intAdd vars:   " + numOfIntAdds,
                        "number of all vars:      " + allVars.size(),
                        "number of addr. vars:    " + addressedVariables.size(),
                        "number of intBool partitions:  " + vc.getIntBoolPartitions().size(),
                        "number of intEq partitions:    " + vc.getIntEqualPartitions().size(),
                        "number of intAdd partitions:   " + vc.getIntAddPartitions().size(),
                        "number of all partitions:      " + dependencies.partitions.size(), });
        str.append("\n---------------------------------\n");

        logger.log(Level.INFO, str.toString());
    }

    private int countNumberOfRelevantVars(Set<String> ofVars) {
        return Sets.intersection(ofVars, relevantVariables).size();
    }

    /** This function iterates over all edges of the cfa, collects all variables
     * and orders them into different sets, i.e. nonBoolean and nonIntEuqalNumber. */
    private void collectVars(CFA cfa) throws UnrecognizedCCodeException {
        Collection<CFANode> nodes = cfa.getAllNodes();
        for (CFANode node : nodes) {
            for (CFAEdge edge : leavingEdges(node)) {
                handleEdge(edge, cfa);
            }
        }
    }

    private void propagateRelevancy() {
        // Propagate relevant variables from assumes and assignments to pointer dereferences to
        // other variables up to a fix-point (actually as the direction of dependency doesn't matter
        // it's just a BFS)
        Queue<VariableOrField> queue = new ArrayDeque<>(relevantVariables.size() + relevantFields.size());
        for (final String relevantVariable : relevantVariables) {
            queue.add(VariableOrField.newVariable(relevantVariable));
        }
        for (final Map.Entry<CCompositeType, String> relevantField : relevantFields.entries()) {
            queue.add(VariableOrField.newField(relevantField.getKey(), relevantField.getValue()));
        }
        while (!queue.isEmpty()) {
            final VariableOrField relevantVariableOrField = queue.poll();
            for (VariableOrField variableOrField : assignments.get(relevantVariableOrField)) {
                final VariableOrField.Variable variable = variableOrField.asVariable();
                final VariableOrField.Field field = variableOrField.asField();
                assert variable != null || field != null : "Sum type match failure: neither variable nor field!";
                if (variable != null && !relevantVariables.contains(variable.getScopedName())) {
                    relevantVariables.add(variable.getScopedName());
                    queue.add(variable);
                } else if (field != null
                        && !relevantFields.containsEntry(field.getCompositeType(), field.getName())) {
                    relevantFields.put(field.getCompositeType(), field.getName());
                    queue.add(field);
                }
            }
        }
    }

    private static CCompositeType getCanonicalFieldOwnerType(CFieldReference fieldReference) {
        CType fieldOwnerType = fieldReference.getFieldOwner().getExpressionType().getCanonicalType();

        if (fieldOwnerType instanceof CPointerType) {
            fieldOwnerType = ((CPointerType) fieldOwnerType).getType();
        }
        assert fieldOwnerType instanceof CCompositeType : "Field owner should have composite type, but the field-owner type of expression "
                + fieldReference + " in " + fieldReference.getFileLocation() + " is " + fieldOwnerType
                + ", which is a " + fieldOwnerType.getClass().getSimpleName() + ".";
        final CCompositeType compositeType = (CCompositeType) fieldOwnerType;
        // Currently we don't pay attention to possible const and volatile modifiers
        if (compositeType.isConst() || compositeType.isVolatile()) {
            return new CCompositeType(false, false, compositeType.getKind(), compositeType.getMembers(),
                    compositeType.getName());
        } else {
            return compositeType;
        }
    }

    /** switch to edgeType and handle all expressions, that could be part of the edge. */
    private void handleEdge(CFAEdge edge, CFA cfa) throws UnrecognizedCCodeException {
        switch (edge.getEdgeType()) {

        case AssumeEdge: {
            CExpression exp = ((CAssumeEdge) edge).getExpression();
            CFANode pre = edge.getPredecessor();

            VariablesCollectingVisitor dcv = new VariablesCollectingVisitor(pre);
            Set<String> vars = exp.accept(dcv);
            if (vars != null) {
                allVars.addAll(vars);
                dependencies.addAll(vars, dcv.getValues(), edge, 0);
            }

            exp.accept(new BoolCollectingVisitor(pre));
            exp.accept(new IntEqualCollectingVisitor(pre));
            exp.accept(new IntAddCollectingVisitor(pre));

            exp.accept(new CollectingRHSVisitor(null));
            break;
        }

        case DeclarationEdge: {
            handleDeclarationEdge((CDeclarationEdge) edge);
            break;
        }

        case StatementEdge: {
            final CStatement statement = ((CStatementEdge) edge).getStatement();

            // normal assignment of variable, rightHandSide can be expression or (external) functioncall
            if (statement instanceof CAssignment) {
                handleAssignment(edge, (CAssignment) statement, cfa);

                // pure external functioncall
            } else if (statement instanceof CFunctionCallStatement) {
                handleExternalFunctionCall(edge,
                        ((CFunctionCallStatement) statement).getFunctionCallExpression().getParameterExpressions());

                ((CFunctionCallStatement) statement).getFunctionCallExpression()
                        .accept(new CollectingRHSVisitor(null));
            }

            break;
        }

        case FunctionCallEdge: {
            handleFunctionCallEdge((CFunctionCallEdge) edge);
            break;
        }

        case FunctionReturnEdge: {
            Optional<CVariableDeclaration> returnVar = ((CFunctionReturnEdge) edge).getFunctionEntry()
                    .getReturnVariable();
            if (returnVar.isPresent()) {
                String scopedVarName = returnVar.get().getQualifiedName();
                dependencies.addVar(scopedVarName);
                Partition partition = dependencies.getPartitionForVar(scopedVarName);
                partition.addEdge(edge, 0);
            }
            break;
        }

        case ReturnStatementEdge: {
            // this is the 'x' from 'return (x);
            // adding a new temporary FUNCTION_RETURN_VARIABLE, that is not global (-> false)
            CReturnStatementEdge returnStatement = (CReturnStatementEdge) edge;
            if (returnStatement.asAssignment().isPresent()) {
                handleAssignment(edge, returnStatement.asAssignment().get(), cfa);
            }
            break;
        }

        case MultiEdge:
            for (CFAEdge innerEdge : (MultiEdge) edge) {
                handleEdge(innerEdge, cfa);
            }
            break;

        case BlankEdge:
        case CallToReturnEdge:
            // other cases are not interesting
            break;

        default:
            throw new UnrecognizedCCodeException("Unknown edgeType: " + edge.getEdgeType(), edge);
        }
    }

    /** This function handles a declaration with an optional initializer.
     * Only simple types are handled. */
    private void handleDeclarationEdge(final CDeclarationEdge edge) throws UnrecognizedCCodeException {
        CDeclaration declaration = edge.getDeclaration();
        if (!(declaration instanceof CVariableDeclaration)) {
            return;
        }

        CVariableDeclaration vdecl = (CVariableDeclaration) declaration;
        String varName = vdecl.getQualifiedName();
        allVars.add(varName);

        // "connect" the edge with its partition
        Set<String> var = Sets.newHashSetWithExpectedSize(1);
        var.add(varName);
        dependencies.addAll(var, new HashSet<BigInteger>(), edge, 0);

        // only simple types (int, long) are allowed for booleans, ...
        if (!(vdecl.getType() instanceof CSimpleType)) {
            nonIntBoolVars.add(varName);
            nonIntEqVars.add(varName);
            nonIntAddVars.add(varName);
        }

        final CInitializer initializer = vdecl.getInitializer();
        List<CExpressionAssignmentStatement> l = CInitializers.convertToAssignments(vdecl, edge);

        for (CExpressionAssignmentStatement init : l) {
            final CLeftHandSide lhsExpression = init.getLeftHandSide();
            final VariableOrField lhs = lhsExpression.accept(collectingLHSVisitor);

            final CExpression rhs = init.getRightHandSide();
            rhs.accept(new CollectingRHSVisitor(lhs));
        }

        if ((initializer == null) || !(initializer instanceof CInitializerExpression)) {
            return;
        }

        CExpression exp = ((CInitializerExpression) initializer).getExpression();
        if (exp == null) {
            return;
        }

        handleExpression(edge, exp, varName, VariableOrField.newVariable(varName));
    }

    /** This function handles normal assignments of vars. */
    private void handleAssignment(final CFAEdge edge, final CAssignment assignment, final CFA cfa)
            throws UnrecognizedCCodeException {
        CRightHandSide rhs = assignment.getRightHandSide();
        CExpression lhs = assignment.getLeftHandSide();
        String function = isGlobal(lhs) ? null : edge.getPredecessor().getFunctionName();
        String varName = scopeVar(function, lhs.toASTString());

        // only simple types (int, long) are allowed for booleans, ...
        if (!(lhs instanceof CIdExpression && lhs.getExpressionType() instanceof CSimpleType)) {
            nonIntBoolVars.add(varName);
            nonIntEqVars.add(varName);
            nonIntAddVars.add(varName);
        }

        dependencies.addVar(varName);

        final VariableOrField lhsVariableOrField = lhs.accept(collectingLHSVisitor);

        if (rhs instanceof CExpression) {
            handleExpression(edge, ((CExpression) rhs), varName, lhsVariableOrField);

        } else if (rhs instanceof CFunctionCallExpression) {
            // use FUNCTION_RETURN_VARIABLE for RIGHT SIDE
            CFunctionCallExpression func = (CFunctionCallExpression) rhs;
            String functionName = func.getFunctionNameExpression().toASTString(); // TODO correct?

            if (cfa.getAllFunctionNames().contains(functionName)) {
                Optional<? extends AVariableDeclaration> returnVariable = cfa.getFunctionHead(functionName)
                        .getReturnVariable();
                if (!returnVariable.isPresent()) {
                    throw new UnrecognizedCCodeException("Void function " + functionName + " used in assignment",
                            edge, assignment);
                }
                String returnVar = returnVariable.get().getQualifiedName();
                allVars.add(returnVar);
                allVars.add(varName);
                dependencies.add(returnVar, varName);

            } else {
                // external function
                Partition partition = dependencies.getPartitionForVar(varName);
                partition.addEdge(edge, -1); // negative value, because all positives are used for params
            }

            rhs.accept(new CollectingRHSVisitor(lhsVariableOrField));

            handleExternalFunctionCall(edge, func.getParameterExpressions());

        } else {
            throw new UnrecognizedCCodeException("unhandled assignment", edge, assignment);
        }
    }

    /** This function handles the call of an external function
     * without an assignment of the result.
     * example: "printf("%d", output);" or "assert(exp);" */
    private void handleExternalFunctionCall(final CFAEdge edge, final List<CExpression> params) {
        for (int i = 0; i < params.size(); i++) {
            final CExpression param = params.get(i);

            /* special case: external functioncall with possible side-effect!
             * this is the only statement, where a pointer-operation is allowed
             * and the var can be boolean, intEqual or intAdd,
             * because we know, the variable can have a random (unknown) value after the functioncall.
             * example: "scanf("%d", &input);" */
            if (param instanceof CUnaryExpression && UnaryOperator.AMPER == ((CUnaryExpression) param).getOperator()
                    && ((CUnaryExpression) param).getOperand() instanceof CIdExpression) {
                final CIdExpression id = (CIdExpression) ((CUnaryExpression) param).getOperand();
                final String varName = id.getDeclaration().getQualifiedName();

                dependencies.addVar(varName);
                Partition partition = dependencies.getPartitionForVar(varName);
                partition.addEdge(edge, i);

            } else {
                // "printf("%d", output);" or "assert(exp);"
                // TODO do we need the edge? ignore it?

                CFANode pre = edge.getPredecessor();
                VariablesCollectingVisitor dcv = new VariablesCollectingVisitor(pre);
                Set<String> vars = param.accept(dcv);
                if (vars != null) {
                    allVars.addAll(vars);
                    dependencies.addAll(vars, dcv.getValues(), edge, i);
                }

                param.accept(new BoolCollectingVisitor(pre));
                param.accept(new IntEqualCollectingVisitor(pre));
                param.accept(new IntAddCollectingVisitor(pre));
            }
        }
    }

    /** This function puts each param in same partition than its arg.
     * If there the functionresult is assigned, it is also handled. */
    private void handleFunctionCallEdge(CFunctionCallEdge edge) {

        // overtake arguments from last functioncall into function,
        // get args from functioncall and make them equal with params from functionstart
        final List<CExpression> args = edge.getArguments();
        final List<CParameterDeclaration> params = edge.getSuccessor().getFunctionParameters();

        // functions can have more args than params used in the call
        assert args.size() >= params.size();

        for (int i = 0; i < params.size(); i++) {
            CParameterDeclaration param = params.get(i);
            String varName = param.getQualifiedName();

            // only simple types (int, long) are allowed for booleans, ...
            if (!(param.getType() instanceof CSimpleType)) {
                nonIntBoolVars.add(varName);
                nonIntEqVars.add(varName);
                nonIntAddVars.add(varName);
            }

            // build name for param and evaluate it
            // this variable is not global (->false)
            handleExpression(edge, args.get(i), varName, i, VariableOrField.newVariable(varName));
        }

        // create dependency for functionreturn
        CFunctionSummaryEdge func = edge.getSummaryEdge();
        CFunctionCall statement = func.getExpression();
        Optional<CVariableDeclaration> returnVar = edge.getSuccessor().getReturnVariable();
        if (returnVar.isPresent()) {
            String scopedRetVal = returnVar.get().getQualifiedName();
            if (statement instanceof CFunctionCallAssignmentStatement) {
                // a=f();
                CFunctionCallAssignmentStatement call = (CFunctionCallAssignmentStatement) statement;
                CExpression lhs = call.getLeftHandSide();
                String function = isGlobal(lhs) ? null : edge.getPredecessor().getFunctionName();
                String varName = scopeVar(function, lhs.toASTString());
                allVars.add(scopedRetVal);
                allVars.add(varName);
                dependencies.add(scopedRetVal, varName);

                final VariableOrField lhsVariableOrField = lhs.accept(collectingLHSVisitor);

                assignments.put(lhsVariableOrField, VariableOrField.newVariable(scopedRetVal));

            } else if (statement instanceof CFunctionCallStatement) {
                // f(); without assignment
                // next line is not necessary, but we do it for completeness, TODO correct?
                dependencies.addVar(scopedRetVal);
            }
        }
    }

    /** evaluates an expression and adds containing vars to the sets. */
    private void handleExpression(CFAEdge edge, CExpression exp, String varName, final VariableOrField lhs) {
        handleExpression(edge, exp, varName, 0, lhs);
    }

    /** evaluates an expression and adds containing vars to the sets.
     * the id is the position of the expression in the edge,
     * it is 0 for all edges except a FuntionCallEdge. */
    private void handleExpression(CFAEdge edge, CExpression exp, String varName, int id,
            final VariableOrField lhs) {
        CFANode pre = edge.getPredecessor();

        VariablesCollectingVisitor dcv = new VariablesCollectingVisitor(pre);
        Set<String> vars = exp.accept(dcv);
        if (vars == null) {
            vars = Sets.newHashSetWithExpectedSize(1);
        }

        vars.add(varName);
        allVars.addAll(vars);
        dependencies.addAll(vars, dcv.getValues(), edge, id);

        BoolCollectingVisitor bcv = new BoolCollectingVisitor(pre);
        Set<String> possibleBoolean = exp.accept(bcv);
        handleResult(varName, possibleBoolean, nonIntBoolVars);

        IntEqualCollectingVisitor ncv = new IntEqualCollectingVisitor(pre);
        Set<String> possibleIntEqualVars = exp.accept(ncv);
        handleResult(varName, possibleIntEqualVars, nonIntEqVars);

        IntAddCollectingVisitor icv = new IntAddCollectingVisitor(pre);
        Set<String> possibleIntAddVars = exp.accept(icv);
        handleResult(varName, possibleIntAddVars, nonIntAddVars);

        exp.accept(new CollectingRHSVisitor(lhs));
    }

    /** adds the variable to notPossibleVars, if possibleVars is null.  */
    private void handleResult(String varName, Collection<String> possibleVars, Collection<String> notPossibleVars) {
        if (possibleVars == null) {
            notPossibleVars.add(varName);
        }
    }

    private static String scopeVar(@Nullable final String function, final String var) {
        return (function == null) ? (var) : (function + SCOPE_SEPARATOR + var);
    }

    private static boolean isGlobal(CExpression exp) {
        if (exp instanceof CIdExpression) {
            CSimpleDeclaration decl = ((CIdExpression) exp).getDeclaration();
            if (decl instanceof CDeclaration) {
                return ((CDeclaration) decl).isGlobal();
            }
        }
        return false;
    }

    /**
     * Use {@link FunctionEntryNode#getReturnVariable()} and
     * {@link AReturnStatement#asAssignment()} instead.
     */
    @Deprecated
    public static String createFunctionReturnVariable(final String function) {
        return function + SCOPE_SEPARATOR + FUNCTION_RETURN_VARIABLE;
    }

    /** returns the value of a (nested) IntegerLiteralExpression
     * or null for everything else. */
    public static BigInteger getNumber(CExpression exp) {
        if (exp instanceof CIntegerLiteralExpression) {
            return ((CIntegerLiteralExpression) exp).getValue();

        } else if (exp instanceof CUnaryExpression) {
            CUnaryExpression unExp = (CUnaryExpression) exp;
            BigInteger value = getNumber(unExp.getOperand());
            if (value == null) {
                return null;
            }
            switch (unExp.getOperator()) {
            case MINUS:
                return value.negate();
            default:
                return null;
            }

        } else if (exp instanceof CCastExpression) {
            return getNumber(((CCastExpression) exp).getOperand());

        } else {
            return null;
        }
    }

    /** returns true, if the expression contains a casted binaryExpression. */
    private static boolean isNestedBinaryExp(CExpression exp) {
        if (exp instanceof CBinaryExpression) {
            return true;

        } else if (exp instanceof CCastExpression) {
            return isNestedBinaryExp(((CCastExpression) exp).getOperand());

        } else {
            return false;
        }
    }

    /** This Visitor evaluates an Expression. It collects all variables.
     * a visit of IdExpression or CFieldReference returns a collection containing the varName,
     * other visits return the inner visit-results.
    * The Visitor also collects all numbers used in the expression. */
    private static class VariablesCollectingVisitor implements CExpressionVisitor<Set<String>, RuntimeException> {

        private CFANode predecessor;
        private Set<BigInteger> values = new TreeSet<>();

        public VariablesCollectingVisitor(CFANode pre) {
            this.predecessor = pre;
        }

        public Set<BigInteger> getValues() {
            return values;
        }

        @Override
        public Set<String> visit(CArraySubscriptExpression exp) {
            return null;
        }

        @Override
        public Set<String> visit(CBinaryExpression exp) {

            // for numeral values
            BigInteger val1 = getNumber(exp.getOperand1());
            Set<String> operand1;
            if (val1 == null) {
                operand1 = exp.getOperand1().accept(this);
            } else {
                values.add(val1);
                operand1 = null;
            }

            // for numeral values
            BigInteger val2 = getNumber(exp.getOperand2());
            Set<String> operand2;
            if (val2 == null) {
                operand2 = exp.getOperand2().accept(this);
            } else {
                values.add(val2);
                operand2 = null;
            }

            // handle vars from operands
            if (operand1 == null) {
                return operand2;
            } else if (operand2 == null) {
                return operand1;
            } else {
                operand1.addAll(operand2);
                return operand1;
            }
        }

        @Override
        public Set<String> visit(CCastExpression exp) {
            BigInteger val = getNumber(exp.getOperand());
            if (val == null) {
                return exp.getOperand().accept(this);
            } else {
                values.add(val);
                return null;
            }
        }

        @Override
        public Set<String> visit(CComplexCastExpression exp) {
            // TODO complex numbers are not supported for evaluation right now, this
            // way of handling the variables my be wrong

            BigInteger val = getNumber(exp.getOperand());
            if (val == null) {
                return exp.getOperand().accept(this);
            } else {
                values.add(val);
                return null;
            }
        }

        @Override
        public Set<String> visit(CFieldReference exp) {
            String varName = exp.toASTString(); // TODO "(*p).x" vs "p->x"
            String function = isGlobal(exp) ? "" : predecessor.getFunctionName();
            Set<String> ret = Sets.newHashSetWithExpectedSize(1);
            ret.add(scopeVar(function, varName));
            return ret;
        }

        @Override
        public Set<String> visit(CIdExpression exp) {
            Set<String> ret = Sets.newHashSetWithExpectedSize(1);
            ret.add(exp.getDeclaration().getQualifiedName());
            return ret;
        }

        @Override
        public Set<String> visit(CCharLiteralExpression exp) {
            return null;
        }

        @Override
        public Set<String> visit(CFloatLiteralExpression exp) {
            return null;
        }

        @Override
        public Set<String> visit(CImaginaryLiteralExpression exp) {
            return exp.getValue().accept(this);
        }

        @Override
        public Set<String> visit(CIntegerLiteralExpression exp) {
            values.add(exp.getValue());
            return null;
        }

        @Override
        public Set<String> visit(CStringLiteralExpression exp) {
            return null;
        }

        @Override
        public Set<String> visit(CTypeIdExpression exp) {
            return null;
        }

        @Override
        public Set<String> visit(CUnaryExpression exp) {
            BigInteger val = getNumber(exp);
            if (val == null) {
                return exp.getOperand().accept(this);
            } else {
                values.add(val);
                return null;
            }
        }

        @Override
        public Set<String> visit(CPointerExpression exp) {
            BigInteger val = getNumber(exp);
            if (val == null) {
                return exp.getOperand().accept(this);
            } else {
                values.add(val);
                return null;
            }
        }
    }

    /** This class stores dependencies between variables.
     * It sorts vars into partitions.
     * Dependent vars are in the same partition. Partitions are independent. */
    private static class Dependencies {

        /** partitions, each of them contains vars */
        private final List<Partition> partitions = Lists.newArrayList();

        /** map to get partition of a var */
        private final Map<String, Partition> varToPartition = Maps.newHashMap();

        /** table to get a partition for a edge. */
        private final Map<Pair<CFAEdge, Integer>, Partition> edgeToPartition = Maps.newHashMap();

        /** This function returns a partition containing all vars,
         * that are dependent with the given variable. */
        public Partition getPartitionForVar(String var) {
            return varToPartition.get(var);
        }

        /** This function creates a dependency between function1::var1 and function2::var2. */
        public void add(String var1, String var2) {

            // if both vars exists in some dependencies,
            // either ignore them or merge their partitions
            Partition partition1 = varToPartition.get(var1);
            Partition partition2 = varToPartition.get(var2);
            if (partition1 != null && partition2 != null) {

                // swap partitions, we create partitions in the order they are used
                if (partitions.lastIndexOf(partition1) > partitions.lastIndexOf(partition2)) {
                    Partition tmp = partition2;
                    partition2 = partition1;
                    partition1 = tmp;
                }

                if (!partition1.equals(partition2)) {
                    partition1.merge(partition2);
                    partitions.remove(partition2);
                }

                // if only left side of dependency exists, add right side into same partition
            } else if (partition1 != null) {
                partition1.add(var2);

                // if only right side of dependency exists, add left side into same partition
            } else if (partition2 != null) {
                partition2.add(var1);

                // if none side is in any existing partition, create new partition
            } else {
                Partition partition = new Partition(varToPartition, edgeToPartition);
                partition.add(var1);
                partition.add(var2);
                partitions.add(partition);
            }
        }

        /** This function adds a group of vars to exactly one partition.
         * The values are stored in the partition.
         * The partition is "connected" with the expression.
         *
         * @param vars group of variables tobe added
         * @param values numbers, with are used in an expression together with the variables
         * @param edge where is the expression
         * @param index if an edge has several expressions, this index is the position ofthe expression
         *  */
        public void addAll(Collection<String> vars, Set<BigInteger> values, CFAEdge edge, int index) {
            if (vars == null || vars.isEmpty()) {
                return;
            }

            Iterator<String> iter = vars.iterator();

            // we use same varName for all other vars --> dependency
            String var = iter.next();

            // first add one single var
            addVar(var);

            // then add all other vars, they are dependent from the first var
            while (iter.hasNext()) {
                add(var, iter.next());
            }

            Partition partition = getPartitionForVar(var);
            partition.addValues(values);
            partition.addEdge(edge, index);
        }

        /** This function adds one single variable to the partitions.
         * This is the only method to create a partition with only one element. */
        public void addVar(String var) {

            // if var exists, we can ignore it, otherwise create new partition for var
            if (!varToPartition.containsKey(var)) {
                Partition partition = new Partition(varToPartition, edgeToPartition);
                partition.add(var);
                partitions.add(partition);
            }
        }

        /** This function adds all depending vars to the set, if necessary.
         * If A depends on B and A is part of the set, B is added to the set, and vice versa.
        * Example: If A is not boolean, B is not boolean. */
        public void solve(final Set<String> vars) {
            for (Partition partition : partitions) {

                // is at least one var from the partition part of vars
                if (!Sets.intersection(partition.getVars(), vars).isEmpty()) {
                    // add all dependend vars to vars
                    vars.addAll(partition.getVars());
                }
            }
        }

        @Override
        public String toString() {
            StringBuilder str = new StringBuilder("[");
            Joiner.on(",\n").appendTo(str, partitions);
            str.append("]\n\n");

            //      for (Pair<CFAEdge, Integer> edge : edgeToPartition.keySet()) {
            //        str.append(edge.getFirst().getRawStatement() + " :: "
            //            + edge.getSecond() + " --> " + edgeToPartition.get(edge) + "\n");
            //      }
            return str.toString();
        }
    }

    /** This Visitor evaluates an Expression. It also collects all variables.
     * Each visit-function returns
     * - null, if the expression is not boolean
     * - a collection, if the expression is boolean.
     * The collection contains all boolean vars. */
    private class BoolCollectingVisitor extends VariablesCollectingVisitor {

        public BoolCollectingVisitor(CFANode pre) {
            super(pre);
        }

        @Override
        public Set<String> visit(CFieldReference exp) {
            nonIntBoolVars.addAll(super.visit(exp));
            return null;
        }

        @Override
        public Set<String> visit(CBinaryExpression exp) {
            Set<String> operand1 = exp.getOperand1().accept(this);
            Set<String> operand2 = exp.getOperand2().accept(this);

            if (operand1 == null || operand2 == null) { // a+123 --> a is not boolean
                if (operand1 != null) {
                    nonIntBoolVars.addAll(operand1);
                }
                if (operand2 != null) {
                    nonIntBoolVars.addAll(operand2);
                }
                return null;
            }

            switch (exp.getOperator()) {

            case EQUALS:
            case NOT_EQUALS: // ==, != work with boolean operands
                if (operand1.isEmpty() || operand2.isEmpty()) {
                    // one operand is Zero (or One, if allowed)
                    operand1.addAll(operand2);
                    return operand1;
                }
                // We compare 2 variables. There is no guarantee, that they are boolean!
                // Example: (a!=b) && (b!=c) && (c!=a)
                // -> FALSE for boolean, but TRUE for {1,2,3}

                //$FALL-THROUGH$

            default: // +-*/ --> no boolean operators, a+b --> a and b are not boolean
                nonIntBoolVars.addAll(operand1);
                nonIntBoolVars.addAll(operand2);
                return null;
            }
        }

        @Override
        public Set<String> visit(CIntegerLiteralExpression exp) {
            BigInteger value = exp.getValue();
            if (BigInteger.ZERO.equals(value) || (allowOneAsBooleanValue && BigInteger.ONE.equals(value))) {
                return new HashSet<>(0);
            } else {
                return null;
            }
        }

        @Override
        public Set<String> visit(CUnaryExpression exp) {
            Set<String> inner = exp.getOperand().accept(this);

            if (inner == null) {
                return null;
            } else { // PLUS, MINUS, etc --> not boolean
                nonIntBoolVars.addAll(inner);
                return null;
            }
        }

        @Override
        public Set<String> visit(CPointerExpression exp) {
            Set<String> inner = exp.getOperand().accept(this);

            if (inner == null) {
                return null;
            } else {
                nonIntBoolVars.addAll(inner);
                return null;
            }
        }
    }

    /** This Visitor evaluates an Expression.
     * Each visit-function returns
     * - null, if the expression contains calculations
     * - a collection, if the expression is a number, unaryExp, == or != */
    private class IntEqualCollectingVisitor extends VariablesCollectingVisitor {

        public IntEqualCollectingVisitor(CFANode pre) {
            super(pre);
        }

        @Override
        public Set<String> visit(CCastExpression exp) {
            BigInteger val = getNumber(exp.getOperand());
            if (val == null) {
                return exp.getOperand().accept(this);
            } else {
                return new HashSet<>(0);
            }
        }

        @Override
        public Set<String> visit(CFieldReference exp) {
            nonIntEqVars.addAll(super.visit(exp));
            return null;
        }

        @Override
        public Set<String> visit(CBinaryExpression exp) {

            // for numeral values
            BigInteger val1 = getNumber(exp.getOperand1());
            Set<String> operand1;
            if (val1 == null) {
                operand1 = exp.getOperand1().accept(this);
            } else {
                operand1 = new HashSet<>(0);
            }

            // for numeral values
            BigInteger val2 = getNumber(exp.getOperand2());
            Set<String> operand2;
            if (val2 == null) {
                operand2 = exp.getOperand2().accept(this);
            } else {
                operand2 = new HashSet<>(0);
            }

            // handle vars from operands
            if (operand1 == null || operand2 == null) { // a+0.2 --> no simple number
                if (operand1 != null) {
                    nonIntEqVars.addAll(operand1);
                }
                if (operand2 != null) {
                    nonIntEqVars.addAll(operand2);
                }
                return null;
            }

            switch (exp.getOperator()) {

            case EQUALS:
            case NOT_EQUALS: // ==, != work with numbers
                operand1.addAll(operand2);
                return operand1;

            default: // +-*/ --> no simple operators
                nonIntEqVars.addAll(operand1);
                nonIntEqVars.addAll(operand2);
                return null;
            }
        }

        @Override
        public Set<String> visit(CIntegerLiteralExpression exp) {
            return new HashSet<>(0);
        }

        @Override
        public Set<String> visit(CUnaryExpression exp) {

            // if exp is numeral
            BigInteger val = getNumber(exp);
            if (val != null) {
                return new HashSet<>(0);
            }

            // if exp is binary expression
            Set<String> inner = exp.getOperand().accept(this);
            if (isNestedBinaryExp(exp)) {
                return inner;
            }

            if (inner != null) {
                nonIntEqVars.addAll(inner);
            }
            return null;
        }

        @Override
        public Set<String> visit(CPointerExpression exp) {

            // if exp is numeral
            BigInteger val = getNumber(exp);
            if (val != null) {
                return new HashSet<>(0);
            }

            // if exp is binary expression
            Set<String> inner = exp.getOperand().accept(this);
            if (isNestedBinaryExp(exp)) {
                return inner;
            }

            // if exp is unknown
            if (inner == null) {
                return null;
            }

            nonIntEqVars.addAll(inner);
            return null;
        }
    }

    /** This Visitor evaluates an Expression.
     * Each visit-function returns
     * - a collection, if the expression is a var or a simple mathematical
     *   calculation (add, sub, <, >, <=, >=, ==, !=, !),
     * - else null */
    private class IntAddCollectingVisitor extends VariablesCollectingVisitor {

        public IntAddCollectingVisitor(CFANode pre) {
            super(pre);
        }

        @Override
        public Set<String> visit(CCastExpression exp) {
            return exp.getOperand().accept(this);
        }

        @Override
        public Set<String> visit(CFieldReference exp) {
            nonIntAddVars.addAll(super.visit(exp));
            return null;
        }

        @Override
        public Set<String> visit(CBinaryExpression exp) {
            Set<String> operand1 = exp.getOperand1().accept(this);
            Set<String> operand2 = exp.getOperand2().accept(this);

            if (operand1 == null || operand2 == null) { // a+0.2 --> no simple number
                if (operand1 != null) {
                    nonIntAddVars.addAll(operand1);
                }
                if (operand2 != null) {
                    nonIntAddVars.addAll(operand2);
                }
                return null;
            }

            switch (exp.getOperator()) {

            case PLUS:
            case MINUS:
            case LESS_THAN:
            case LESS_EQUAL:
            case GREATER_THAN:
            case GREATER_EQUAL:
            case EQUALS:
            case NOT_EQUALS:
            case BINARY_AND:
            case BINARY_XOR:
            case BINARY_OR:
                // this calculations work with all numbers
                operand1.addAll(operand2);
                return operand1;

            default: // *, /, %, shift --> no simple calculations
                nonIntAddVars.addAll(operand1);
                nonIntAddVars.addAll(operand2);
                return null;
            }
        }

        @Override
        public Set<String> visit(CIntegerLiteralExpression exp) {
            return new HashSet<>(0);
        }

        @Override
        public Set<String> visit(CUnaryExpression exp) {
            Set<String> inner = exp.getOperand().accept(this);
            if (inner == null) {
                return null;
            }
            if (exp.getOperator() == UnaryOperator.MINUS) {
                return inner;
            }

            // *, ~, etc --> not simple
            nonIntAddVars.addAll(inner);
            return null;
        }

        @Override
        public Set<String> visit(CPointerExpression exp) {
            Set<String> inner = exp.getOperand().accept(this);
            if (inner == null) {
                return null;
            }

            nonIntAddVars.addAll(inner);
            return null;
        }
    }

    private class CollectingLHSVisitor extends DefaultCExpressionVisitor<VariableOrField, RuntimeException> {

        @Override
        public VariableOrField visit(final CArraySubscriptExpression e) {
            final VariableOrField result = e.getArrayExpression().accept(this);
            e.getSubscriptExpression().accept(new CollectingRHSVisitor(result));
            return result;
        }

        @Override
        public VariableOrField visit(final CFieldReference e) {
            final CCompositeType compositeType = getCanonicalFieldOwnerType(e);
            final VariableOrField result = VariableOrField.newField(compositeType, e.getFieldName());
            if (e.isPointerDereference()) {
                e.getFieldOwner().accept(new CollectingRHSVisitor(result));
            } else {
                e.getFieldOwner().accept(this);
            }
            return result;
        }

        @Override
        public VariableOrField visit(final CPointerExpression e) {
            e.getOperand().accept(new CollectingRHSVisitor(null));
            return null;
        }

        @Override
        public VariableOrField visit(final CComplexCastExpression e) {
            return e.getOperand().accept(this);
        }

        @Override
        public VariableOrField visit(final CCastExpression e) {
            return e.getOperand().accept(this);
        }

        @Override
        public VariableOrField visit(final CIdExpression e) {
            return VariableOrField.newVariable(e.getDeclaration().getQualifiedName());
        }

        @Override
        protected VariableOrField visitDefault(final CExpression e) {
            throw new IllegalArgumentException("The expression should not occur in the left hand side");
        }
    }

    private void addVariableOrField(final @Nullable VariableOrField lhs, final VariableOrField rhs) {
        if (lhs != null) {
            assignments.put(lhs, rhs);
        } else {
            final VariableOrField.Variable variable = rhs.asVariable();
            final VariableOrField.Field field = rhs.asField();
            if (variable != null) {
                relevantVariables.add(variable.getScopedName());
            } else {
                relevantFields.put(field.getCompositeType(), field.getName());
            }
        }
    }

    private class CollectingRHSVisitor extends DefaultCExpressionVisitor<Void, RuntimeException>
            implements CRightHandSideVisitor<Void, RuntimeException> {

        private final @Nullable VariableOrField lhs;
        private boolean addressed = false;

        CollectingRHSVisitor(@Nullable VariableOrField pLhs) {
            lhs = pLhs;
        }

        @Override
        public Void visit(final CArraySubscriptExpression e) {
            CollectingRHSVisitor arrayExprVisitor = new CollectingRHSVisitor(null);
            arrayExprVisitor.addressed = true;
            e.getArrayExpression().accept(arrayExprVisitor);
            return e.getSubscriptExpression().accept(this);
        }

        @Override
        public Void visit(final CFieldReference e) {
            final CCompositeType compositeType = getCanonicalFieldOwnerType(e);
            addVariableOrField(lhs, VariableOrField.newField(compositeType, e.getFieldName()));
            return e.getFieldOwner().accept(this);
        }

        @Override
        public Void visit(final CBinaryExpression e) {
            e.getOperand1().accept(this);
            return e.getOperand2().accept(this);
        }

        @Override
        public Void visit(final CUnaryExpression e) {
            if (e.getOperator() != UnaryOperator.AMPER) {
                return e.getOperand().accept(this);
            } else {
                addressed = true;
                e.getOperand().accept(this);
                addressed = false;
                return null;
            }
        }

        @Override
        public Void visit(final CPointerExpression e) {
            return e.getOperand().accept(this);
        }

        @Override
        public Void visit(final CComplexCastExpression e) {
            return e.getOperand().accept(this);
        }

        @Override
        public Void visit(final CCastExpression e) {
            return e.getOperand().accept(this);
        }

        @Override
        public Void visit(final CIdExpression e) {
            final VariableOrField.Variable variable = VariableOrField
                    .newVariable(e.getDeclaration().getQualifiedName());
            addVariableOrField(lhs, variable);
            if (addressed) {
                addressedVariables.add(variable.getScopedName());
            }
            return null;
        }

        @Override
        public Void visit(CFunctionCallExpression e) throws RuntimeException {
            for (CExpression param : e.getParameterExpressions()) {
                param.accept(this);
            }
            return null;
        }

        @Override
        protected Void visitDefault(final CExpression e) {
            return null;
        }
    }

    private static class VariableOrField {
        private static class Variable extends VariableOrField {
            private Variable(final @Nonnull String scopedName) {
                this.scopedName = scopedName;
            }

            public @Nonnull String getScopedName() {
                return scopedName;
            }

            @Override
            public String toString() {
                return getScopedName();
            }

            @Override
            public boolean equals(final Object o) {
                if (o == this) {
                    return true;
                } else if (!(o instanceof Variable)) {
                    return false;
                } else {
                    final Variable other = (Variable) o;
                    return this.scopedName.equals(other.scopedName);
                }
            }

            @Override
            public int hashCode() {
                return scopedName.hashCode();
            }

            private final @Nonnull String scopedName;
        }

        private static class Field extends VariableOrField {
            private Field(final CCompositeType composite, final String name) {
                this.composite = composite;
                this.name = name;
            }

            public CCompositeType getCompositeType() {
                return composite;
            }

            public String getName() {
                return name;
            }

            @Override
            public String toString() {
                return composite + SCOPE_SEPARATOR + name;
            }

            @Override
            public boolean equals(final Object o) {
                if (o == this) {
                    return true;
                } else if (!(o instanceof Field)) {
                    return false;
                } else {
                    final Field other = (Field) o;
                    return this.composite.equals(other.composite) && this.name.equals(other.name);
                }
            }

            @Override
            public int hashCode() {
                final int prime = 67;
                return prime * composite.hashCode() + name.hashCode();
            }

            private @Nonnull CCompositeType composite;
            private @Nonnull String name;
        }

        public static Variable newVariable(final String scopedName) {
            return new Variable(scopedName);
        }

        public static Field newField(final @Nonnull CCompositeType composite, final @Nonnull String name) {
            return new Field(composite, name);
        }

        public @Nullable Variable asVariable() {
            if (this instanceof Variable) {
                return (Variable) this;
            } else {
                return null;
            }
        }

        public @Nullable Field asField() {
            if (this instanceof Field) {
                return (Field) this;
            } else {
                return null;
            }
        }
    }
}