diff --git a/README.md b/README.md index 5afeaf063a..06b32b4a62 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ import de.fraunhofer.aisec.cpg.TranslationManager; import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration; var path = Paths.get("src/test/resources/openssl/client.cpp"); -var config = TranslationConfiguration.builder().sourceLocations(path.toFile()).defaultPasses().debugParser(true).build(); +var config = TranslationConfiguration.builder().sourceLocations(path.toFile()).defaultPasses().defaultLanguages().debugParser(true).build(); var analyzer = TranslationManager.builder().config(config).build(); var result = analyzer.analyze().get(); var tu = result.getTranslationUnits().get(0); diff --git a/build.gradle.kts b/build.gradle.kts index 68b6d86224..63e87f78ae 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -36,7 +36,7 @@ plugins { id("org.sonarqube") version "3.1.1" id("com.diffplug.spotless") version "5.12.1" id("com.github.johnrengelman.shadow") version "6.1.0" - kotlin("jvm") version "1.4.20" + kotlin("jvm") version "1.4.32" } tasks.jacocoTestReport { diff --git a/src/main/java/de/fraunhofer/aisec/cpg/TranslationConfiguration.java b/src/main/java/de/fraunhofer/aisec/cpg/TranslationConfiguration.java index 7da4c0f635..db7d2f8bd8 100644 --- a/src/main/java/de/fraunhofer/aisec/cpg/TranslationConfiguration.java +++ b/src/main/java/de/fraunhofer/aisec/cpg/TranslationConfiguration.java @@ -26,6 +26,14 @@ package de.fraunhofer.aisec.cpg; +import static de.fraunhofer.aisec.cpg.frontends.cpp.CXXLanguageFrontend.CXX_EXTENSIONS; +import static de.fraunhofer.aisec.cpg.frontends.cpp.CXXLanguageFrontend.CXX_HEADER_EXTENSIONS; + +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import de.fraunhofer.aisec.cpg.frontends.LanguageFrontend; +import de.fraunhofer.aisec.cpg.frontends.cpp.CXXLanguageFrontend; +import de.fraunhofer.aisec.cpg.frontends.java.JavaLanguageFrontend; import de.fraunhofer.aisec.cpg.passes.CallResolver; import de.fraunhofer.aisec.cpg.passes.ControlFlowSensitiveDFGPass; import de.fraunhofer.aisec.cpg.passes.EvaluationOrderGraphPass; @@ -87,6 +95,12 @@ public class TranslationConfiguration { */ public final List includeBlacklist; + /** + * This map contains a list of language frontends classes and the file types they are registered + * for. + */ + private Map, List> frontends; + /** * Switch off cleaning up TypeManager memory after analysis. * @@ -134,6 +148,7 @@ private TranslationConfiguration( List includeWhitelist, List includeBlacklist, List passes, + Map, List> frontends, boolean codeInNodes, boolean processAnnotations, boolean disableCleanup, @@ -148,6 +163,7 @@ private TranslationConfiguration( this.includeWhitelist = includeWhitelist; this.includeBlacklist = includeBlacklist; this.passes = passes != null ? passes : new ArrayList<>(); + this.frontends = frontends; // Make sure to init this AFTER sourceLocations has been set this.codeInNodes = codeInNodes; this.processAnnotations = processAnnotations; @@ -175,6 +191,10 @@ public List getRegisteredPasses() { return this.passes; } + public Map, List> getFrontends() { + return this.frontends; + } + /** * Builds a {@link TranslationConfiguration}. * @@ -192,6 +212,7 @@ public List getRegisteredPasses() { */ public static class Builder { private List sourceLocations = new ArrayList<>(); + private Map, List> frontends = new HashMap<>(); private File topLevel = null; private boolean debugParser = false; private boolean failOnError = false; @@ -322,6 +343,13 @@ public Builder registerPass(@NonNull Pass pass) { return this; } + /** Registers an additional {@link de.fraunhofer.aisec.cpg.frontends.LanguageFrontend}. */ + public Builder registerLanguage( + @NonNull Class frontend, List fileTypes) { + this.frontends.put(frontend, fileTypes); + return this; + } + /** * Register all default {@link Pass}es. * @@ -354,6 +382,22 @@ public Builder defaultPasses() { return this; } + /** + * Register all default languages. + * + * @return + */ + public Builder defaultLanguages() { + registerLanguage( + CXXLanguageFrontend.class, + Lists.newArrayList(Iterables.concat(CXX_EXTENSIONS, CXX_HEADER_EXTENSIONS))); + registerLanguage(JavaLanguageFrontend.class, JavaLanguageFrontend.JAVA_EXTENSIONS); + + // do not register experimental languages by default until we have a release strategy + // registerLanguage(GoLanguageFrontend.class, GoLanguageFrontend.GOLANG_EXTENSIONS); + return this; + } + public Builder codeInNodes(boolean b) { this.codeInNodes = b; return this; @@ -391,6 +435,7 @@ public TranslationConfiguration build() { includeWhitelist, includeBlacklist, passes, + frontends, codeInNodes, processAnnotations, disableCleanup, diff --git a/src/main/java/de/fraunhofer/aisec/cpg/TranslationManager.java b/src/main/java/de/fraunhofer/aisec/cpg/TranslationManager.java index 8350ec134c..fdee9fcc41 100644 --- a/src/main/java/de/fraunhofer/aisec/cpg/TranslationManager.java +++ b/src/main/java/de/fraunhofer/aisec/cpg/TranslationManager.java @@ -26,10 +26,9 @@ package de.fraunhofer.aisec.cpg; -import static de.fraunhofer.aisec.cpg.frontends.LanguageFrontendFactory.CXX_EXTENSIONS; +import static de.fraunhofer.aisec.cpg.frontends.cpp.CXXLanguageFrontend.CXX_EXTENSIONS; import de.fraunhofer.aisec.cpg.frontends.LanguageFrontend; -import de.fraunhofer.aisec.cpg.frontends.LanguageFrontendFactory; import de.fraunhofer.aisec.cpg.frontends.TranslationException; import de.fraunhofer.aisec.cpg.graph.TypeManager; import de.fraunhofer.aisec.cpg.helpers.Benchmark; @@ -39,6 +38,7 @@ import java.io.File; import java.io.IOException; import java.io.PrintWriter; +import java.lang.reflect.InvocationTargetException; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; @@ -53,6 +53,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -208,9 +209,7 @@ private HashSet runFrontends( log.info("Parsing {}", sourceLocation.getAbsolutePath()); LanguageFrontend frontend = null; try { - frontend = - LanguageFrontendFactory.getFrontend( - Util.getExtension(sourceLocation), config, scopeManager); + frontend = getFrontend(Util.getExtension(sourceLocation), scopeManager); if (frontend == null) { log.error("Found no parser frontend for {}", sourceLocation.getName()); @@ -258,6 +257,32 @@ private HashSet runFrontends( return usedFrontends; } + @Nullable + private LanguageFrontend getFrontend(String extension, ScopeManager scopeManager) { + var clazz = + this.config.getFrontends().entrySet().stream() + .filter(entry -> entry.getValue().contains(extension)) + .map(entry -> entry.getKey()) + .findAny() + .orElse(null); + + if (clazz != null) { + try { + return clazz + .getConstructor(TranslationConfiguration.class, ScopeManager.class) + .newInstance(this.config, scopeManager); + } catch (InstantiationException + | IllegalAccessException + | InvocationTargetException + | NoSuchMethodException e) { + log.error("Could not instantiate language frontend {}", clazz.getName(), e); + return null; + } + } + + return null; + } + /** * Returns the current (immutable) configuration of this TranslationManager. * diff --git a/src/main/java/de/fraunhofer/aisec/cpg/frontends/LanguageFrontend.java b/src/main/java/de/fraunhofer/aisec/cpg/frontends/LanguageFrontend.java index a104fd9cc0..03e30da703 100644 --- a/src/main/java/de/fraunhofer/aisec/cpg/frontends/LanguageFrontend.java +++ b/src/main/java/de/fraunhofer/aisec/cpg/frontends/LanguageFrontend.java @@ -52,7 +52,9 @@ public abstract class LanguageFrontend { // Allow non-Java frontends to access the logger (i.e. jep) public static final Logger log = LoggerFactory.getLogger(LanguageFrontend.class); protected final TranslationConfiguration config; + protected ScopeManager scopeManager; + /** * Two data structures used to associate Objects input to a pass to results of a pass, e.g. * Javaparser AST-Nodes to CPG-Nodes. The "Listeners" in processedListener are called after the diff --git a/src/main/java/de/fraunhofer/aisec/cpg/frontends/LanguageFrontendFactory.java b/src/main/java/de/fraunhofer/aisec/cpg/frontends/LanguageFrontendFactory.java deleted file mode 100644 index 0c6c935788..0000000000 --- a/src/main/java/de/fraunhofer/aisec/cpg/frontends/LanguageFrontendFactory.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2019, Fraunhofer AISEC. 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. - * - * $$$$$$\ $$$$$$$\ $$$$$$\ - * $$ __$$\ $$ __$$\ $$ __$$\ - * $$ / \__|$$ | $$ |$$ / \__| - * $$ | $$$$$$$ |$$ |$$$$\ - * $$ | $$ ____/ $$ |\_$$ | - * $$ | $$\ $$ | $$ | $$ | - * \$$$$$ |$$ | \$$$$$ | - * \______/ \__| \______/ - * - */ - -package de.fraunhofer.aisec.cpg.frontends; - -import de.fraunhofer.aisec.cpg.TranslationConfiguration; -import de.fraunhofer.aisec.cpg.frontends.cpp.CXXLanguageFrontend; -import de.fraunhofer.aisec.cpg.frontends.golang.GoLanguageFrontend; -import de.fraunhofer.aisec.cpg.frontends.java.JavaLanguageFrontend; -import de.fraunhofer.aisec.cpg.frontends.python.PythonLanguageFrontend; -import de.fraunhofer.aisec.cpg.passes.scopes.ScopeManager; -import java.util.List; -import org.checkerframework.checker.nullness.qual.Nullable; - -public class LanguageFrontendFactory { - - private static final List JAVA_EXTENSIONS = List.of(".java"); - public static final List CXX_EXTENSIONS = List.of(".c", ".cpp", ".cc"); - private static final List CXX_HEADER_EXTENSIONS = List.of(".h", ".hpp"); - private static final List PYTHON_EXTENSIONS = List.of(".py"); - private static final List GOLANG_EXTENSIONS = List.of(".go"); - - // hide ctor - private LanguageFrontendFactory() {} - - @Nullable - public static LanguageFrontend getFrontend( - String fileType, TranslationConfiguration config, ScopeManager scopeManager) { - - if (JAVA_EXTENSIONS.contains(fileType)) { - return new JavaLanguageFrontend(config, scopeManager); - } else if (CXX_EXTENSIONS.contains(fileType) || CXX_HEADER_EXTENSIONS.contains(fileType)) { - return new CXXLanguageFrontend(config, scopeManager); - } else if (PYTHON_EXTENSIONS.contains(fileType)) { - return new PythonLanguageFrontend(config, scopeManager); - } else if (GOLANG_EXTENSIONS.contains(fileType)) { - return new GoLanguageFrontend(config, scopeManager); - } else { - return null; - } - } -} diff --git a/src/main/java/de/fraunhofer/aisec/cpg/frontends/cpp/CXXLanguageFrontend.java b/src/main/java/de/fraunhofer/aisec/cpg/frontends/cpp/CXXLanguageFrontend.java index dec35d62dc..ae28aec9ad 100644 --- a/src/main/java/de/fraunhofer/aisec/cpg/frontends/cpp/CXXLanguageFrontend.java +++ b/src/main/java/de/fraunhofer/aisec/cpg/frontends/cpp/CXXLanguageFrontend.java @@ -96,6 +96,9 @@ */ public class CXXLanguageFrontend extends LanguageFrontend { + public static final List CXX_EXTENSIONS = List.of(".c", ".cpp", ".cc"); + public static final List CXX_HEADER_EXTENSIONS = List.of(".h", ".hpp"); + private static final Logger LOGGER = LoggerFactory.getLogger(CXXLanguageFrontend.class); private final IncludeFileContentProvider includeFileContentProvider = new InternalFileContentProvider() { diff --git a/src/main/java/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontend.kt b/src/main/java/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontend.kt index 61cf275747..a080e45510 100644 --- a/src/main/java/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontend.kt +++ b/src/main/java/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontend.kt @@ -10,12 +10,12 @@ import de.fraunhofer.aisec.cpg.sarif.PhysicalLocation import java.io.File import kotlin.Throws -class RawGolangNode {} - @ExperimentalGolang class GoLanguageFrontend(config: TranslationConfiguration, scopeManager: ScopeManager?) : LanguageFrontend(config, scopeManager, ".") { companion object { + @kotlin.jvm.JvmField var GOLANG_EXTENSIONS: List = listOf(".go") + init { System.loadLibrary("cpgo") } diff --git a/src/main/java/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguageFrontend.java b/src/main/java/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguageFrontend.java index ea31aeb88b..586a5fe850 100644 --- a/src/main/java/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguageFrontend.java +++ b/src/main/java/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguageFrontend.java @@ -82,6 +82,8 @@ /** Main parser for ONE Java files. */ public class JavaLanguageFrontend extends LanguageFrontend { + public static final List JAVA_EXTENSIONS = List.of(".java"); + public static final String THIS = "this"; public static final String ANNOTATION_MEMBER_VALUE = "value"; diff --git a/src/main/java/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguageFrontend.kt b/src/main/java/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguageFrontend.kt index 240fd4add8..905bc94b0a 100644 --- a/src/main/java/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguageFrontend.kt +++ b/src/main/java/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguageFrontend.kt @@ -15,6 +15,9 @@ import jep.* @ExperimentalPython class PythonLanguageFrontend(config: TranslationConfiguration, scopeManager: ScopeManager?) : LanguageFrontend(config, scopeManager, ".") { + companion object { + @kotlin.jvm.JvmField var PY_EXTENSIONS: List = listOf(".py") + } @Throws(TranslationException::class) override fun parse(file: File): TranslationUnitDeclaration { diff --git a/src/main/java/de/fraunhofer/aisec/cpg/graph/TypeManager.java b/src/main/java/de/fraunhofer/aisec/cpg/graph/TypeManager.java index 4318a316a9..1559673959 100644 --- a/src/main/java/de/fraunhofer/aisec/cpg/graph/TypeManager.java +++ b/src/main/java/de/fraunhofer/aisec/cpg/graph/TypeManager.java @@ -356,8 +356,8 @@ public boolean isSupertypeOf(Type superType, Type subType) { Class superCls = Class.forName(superType.getTypeName()); Class subCls = Class.forName(subType.getTypeName()); return superCls.isAssignableFrom(subCls); - } catch (ClassNotFoundException e) { - // Not in the class path, can't help here + } catch (ClassNotFoundException | NoClassDefFoundError e) { + // Not in the class path or other linkage exception, can't help here return false; } } diff --git a/src/test/java/de/fraunhofer/aisec/cpg/TestUtils.java b/src/test/java/de/fraunhofer/aisec/cpg/TestUtils.java deleted file mode 100644 index df0440e23a..0000000000 --- a/src/test/java/de/fraunhofer/aisec/cpg/TestUtils.java +++ /dev/null @@ -1,253 +0,0 @@ -/* - * Copyright (c) 2019, Fraunhofer AISEC. 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. - * - * $$$$$$\ $$$$$$$\ $$$$$$\ - * $$ __$$\ $$ __$$\ $$ __$$\ - * $$ / \__|$$ | $$ |$$ / \__| - * $$ | $$$$$$$ |$$ |$$$$\ - * $$ | $$ ____/ $$ |\_$$ | - * $$ | $$\ $$ | $$ | $$ | - * \$$$$$ |$$ | \$$$$$ | - * \______/ \__| \______/ - * - */ - -package de.fraunhofer.aisec.cpg; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; - -import de.fraunhofer.aisec.cpg.graph.Node; -import de.fraunhofer.aisec.cpg.graph.TypeManager; -import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration; -import de.fraunhofer.aisec.cpg.graph.statements.CompoundStatement; -import de.fraunhofer.aisec.cpg.graph.statements.Statement; -import de.fraunhofer.aisec.cpg.helpers.SubgraphWalker; -import de.fraunhofer.aisec.cpg.helpers.Util; -import de.fraunhofer.aisec.cpg.sarif.PhysicalLocation; -import java.io.File; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Collection; -import java.util.List; -import java.util.function.Predicate; -import java.util.stream.Collectors; -import org.apache.commons.lang3.reflect.FieldUtils; -import org.mockito.Mockito; - -public class TestUtils { - - public static S findByUniquePredicate( - Collection nodes, Predicate predicate) { - List results = findByPredicate(nodes, predicate); - assertEquals(1, results.size(), "Expected exactly one node matching the predicate"); - return results.get(0); - } - - public static List findByPredicate( - Collection nodes, Predicate predicate) { - return nodes.stream().filter(predicate).collect(Collectors.toList()); - } - - public static S findByUniqueName(Collection nodes, String name) { - return findByUniquePredicate(nodes, m -> m.getName().equals(name)); - } - - public static List findByName(Collection nodes, String name) { - return findByPredicate(nodes, m -> m.getName().equals(name)); - } - - /** - * Like {@link #analyze(List, Path, boolean)}, but for all files in a directory tree having a - * specific file extension - * - * @param fileExtension All files found in the directory must end on this String. An empty string - * matches all files - * @param topLevel The directory to traverse while looking for files to parse - * @param usePasses Whether the analysis should run passes after the initial phase - * @return A list of {@link TranslationUnitDeclaration} nodes, representing the CPG roots - * @throws Exception Any exception thrown during the parsing process - */ - public static List analyze( - String fileExtension, Path topLevel, boolean usePasses) throws Exception { - List files = - Files.walk(topLevel, Integer.MAX_VALUE) - .map(Path::toFile) - .filter(File::isFile) - .filter(f -> f.getName().endsWith(fileExtension)) - .sorted() - .collect(Collectors.toList()); - return analyze(files, topLevel, usePasses); - } - - /** - * Default way of parsing a list of files into a full CPG. All default passes are applied - * - * @param topLevel The directory to traverse while looking for files to parse - * @param usePasses Whether the analysis should run passes after the initial phase - * @return A list of {@link TranslationUnitDeclaration} nodes, representing the CPG roots - * @throws Exception Any exception thrown during the parsing process - */ - public static List analyze( - List files, Path topLevel, boolean usePasses) throws Exception { - TranslationConfiguration.Builder builder = - TranslationConfiguration.builder() - .sourceLocations(files) - .topLevel(topLevel.toFile()) - .loadIncludes(true) - .disableCleanup() - .debugParser(true) - .failOnError(true); - if (usePasses) { - builder.defaultPasses(); - } - TranslationConfiguration config = builder.build(); - - TranslationManager analyzer = TranslationManager.builder().config(config).build(); - - return analyzer.analyze().get().getTranslationUnits(); - } - - /** - * Default way of parsing a list of files into a full CPG. All default passes are applied - * - * @param builder A {@link TranslationConfiguration.Builder} which contains the configuration - * @return A list of {@link TranslationUnitDeclaration} nodes, representing the CPG roots - * @throws Exception Any exception thrown during the parsing process - */ - public static List analyzeWithBuilder( - TranslationConfiguration.Builder builder) throws Exception { - TranslationConfiguration config = builder.build(); - - TranslationManager analyzer = TranslationManager.builder().config(config).build(); - - return analyzer.analyze().get().getTranslationUnits(); - } - - public static TranslationUnitDeclaration analyzeAndGetFirstTU( - List files, Path topLevel, boolean usePasses) throws Exception { - List translationUnits = analyze(files, topLevel, usePasses); - return translationUnits.stream().findFirst().orElseThrow(); - } - - /** - * Returns the (first) statement at source line nr. - * - *

If a line contains several statements, only the first one is returned. - * - * @param body - * @param line - * @return Statement at source line or null if not present. - */ - public static Node getByLineNr(CompoundStatement body, int line) { - List nodes = subnodesOfType(body, Statement.class); - for (Statement n : nodes) { - PhysicalLocation location = n.getLocation(); - assertNotNull(location); - - if (location.getRegion().getStartLine() == line) { - return n; - } - } - - return null; - } - - static void disableTypeManagerCleanup() throws IllegalAccessException { - TypeManager spy = Mockito.spy(TypeManager.getInstance()); - Mockito.doNothing().when(spy).cleanup(); - FieldUtils.writeStaticField(TypeManager.class, "INSTANCE", spy, true); - } - - /** - * Returns the first element of the specified Class-type {@code specificClass} that has the name - * {@code name} in the list {@code listOfNodes}. - * - * @param Some class that extends {@link Node}. - */ - public static S getOfTypeWithName( - List listOfNodes, Class specificClass, String name) { - List listOfNodesWithName = - Util.filterCast(listOfNodes, specificClass).stream() - .filter(s -> s.getName().equals(name)) - .collect(Collectors.toList()); - if (listOfNodesWithName.isEmpty()) { - return null; - } - // Here we return the first node, if there are more nodes - return listOfNodesWithName.get(0); - } - - /** - * Returns the first element of the specified Class-type {code specifiedClass} that has the name - * {@code name} in the list of nodes that are subnodes of the AST-root node {@code root}. - * - * @param Some class that extends {@link Node}. - */ - public static S getSubnodeOfTypeWithName( - Node root, Class specificClass, String name) { - return getOfTypeWithName(SubgraphWalker.flattenAST(root), specificClass, name); - } - - /** - * Given a root node in the AST graph, all AST children of the node are filtered for a specific - * CPG Node type and returned. - * - * @param node root of the searched AST subtree - * @param specificClass Class type to be searched for - * @param Type variable that allows returning a list of specific type - * @return a List of searched types - */ - public static List subnodesOfType(Node node, Class specificClass) { - return Util.filterCast(SubgraphWalker.flattenAST(node), specificClass).stream() - .filter(Util.distinctByIdentity()) - .collect(Collectors.toList()); - } - - /** - * Similar to {@link TestUtils#subnodesOfType(Node, Class)} but used when working with a list of - * nodes that is already flat. No walking to get childnodes necessary. - */ - public static List subnodesOfType( - Collection roots, Class specificClass) { - return roots.stream() - .map(n -> subnodesOfType(n, specificClass)) - .flatMap(Collection::stream) - .filter(Util.distinctByIdentity()) - .collect(Collectors.toList()); - } - - /** - * Compare the given parameter {@code toCompare} to the start- or end-line of the given node. If - * the node has no location {@code false} is returned. {@code startLine} is used to specify if the - * start-line or end-line of a location is supposed to be used. - * - * @param n - * @param startLine - * @param toCompare - * @return - */ - public static boolean compareLineFromLocationIfExists(Node n, boolean startLine, int toCompare) { - PhysicalLocation loc = n.getLocation(); - if (loc == null) { - return false; - } - if (startLine) { - return loc.getRegion().getStartLine() == toCompare; - } else { - return loc.getRegion().getEndLine() == toCompare; - } - } -} diff --git a/src/test/java/de/fraunhofer/aisec/cpg/TestUtils.kt b/src/test/java/de/fraunhofer/aisec/cpg/TestUtils.kt new file mode 100644 index 0000000000..90da484b10 --- /dev/null +++ b/src/test/java/de/fraunhofer/aisec/cpg/TestUtils.kt @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2019, Fraunhofer AISEC. 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. + * + * $$$$$$\ $$$$$$$\ $$$$$$\ + * $$ __$$\ $$ __$$\ $$ __$$\ + * $$ / \__|$$ | $$ |$$ / \__| + * $$ | $$$$$$$ |$$ |$$$$\ + * $$ | $$ ____/ $$ |\_$$ | + * $$ | $$\ $$ | $$ | $$ | + * \$$$$$ |$$ | \$$$$$ | + * \______/ \__| \______/ + * + */ +package de.fraunhofer.aisec.cpg + +import de.fraunhofer.aisec.cpg.graph.Node +import de.fraunhofer.aisec.cpg.graph.TypeManager +import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration +import de.fraunhofer.aisec.cpg.helpers.SubgraphWalker +import de.fraunhofer.aisec.cpg.helpers.Util +import java.io.File +import java.nio.file.Files +import java.nio.file.Path +import java.util.function.Consumer +import java.util.function.Predicate +import java.util.stream.Collectors +import org.apache.commons.lang3.reflect.FieldUtils +import org.junit.jupiter.api.Assertions +import org.mockito.Mockito + +object TestUtils { + @JvmStatic + fun findByUniquePredicate(nodes: Collection, predicate: Predicate?): S { + val results = findByPredicate(nodes, predicate) + Assertions.assertEquals(1, results.size, "Expected exactly one node matching the predicate") + return results[0] + } + + @JvmStatic + fun findByPredicate(nodes: Collection, predicate: Predicate?): List { + return nodes.stream().filter(predicate).collect(Collectors.toList()) + } + + @JvmStatic + fun findByUniqueName(nodes: Collection, name: String): S { + return findByUniquePredicate(nodes) { m: S -> m!!.name == name } + } + + @JvmStatic + fun findByName(nodes: Collection, name: String): List { + return findByPredicate(nodes) { m: S -> m!!.name == name } + } + + /** + * Like [TestUtils.analyze], but for all files in a directory tree having a specific file + * extension + * + * @param fileExtension All files found in the directory must end on this String. An empty + * string matches all files + * @param topLevel The directory to traverse while looking for files to parse + * @param usePasses Whether the analysis should run passes after the initial phase + * @param configModifier An optional modifier for the config + * + * @return A list of [TranslationUnitDeclaration] nodes, representing the CPG roots + * @throws Exception Any exception thrown during the parsing process + */ + @JvmOverloads + @JvmStatic + @Throws(Exception::class) + fun analyze( + fileExtension: String?, + topLevel: Path, + usePasses: Boolean, + configModifier: Consumer? = null + ): List { + val files = + Files.walk(topLevel, Int.MAX_VALUE) + .map { obj: Path -> obj.toFile() } + .filter { obj: File -> obj.isFile } + .filter { f: File -> f.name.endsWith(fileExtension!!) } + .sorted() + .collect(Collectors.toList()) + return analyze(files, topLevel, usePasses, configModifier) + } + + /** + * Default way of parsing a list of files into a full CPG. All default passes are applied + * + * @param topLevel The directory to traverse while looking for files to parse + * @param usePasses Whether the analysis should run passes after the initial phase + * @param configModifier An optional modifier for the config + * + * @return A list of [TranslationUnitDeclaration] nodes, representing the CPG roots + * @throws Exception Any exception thrown during the parsing process + */ + @JvmOverloads + @JvmStatic + @Throws(Exception::class) + fun analyze( + files: List?, + topLevel: Path, + usePasses: Boolean, + configModifier: Consumer? = null + ): List { + val builder = + TranslationConfiguration.builder() + .sourceLocations(files) + .topLevel(topLevel.toFile()) + .loadIncludes(true) + .disableCleanup() + .debugParser(true) + .failOnError(true) + .defaultLanguages() + if (usePasses) { + builder.defaultPasses() + } + configModifier?.accept(builder) + val config = builder.build() + val analyzer = TranslationManager.builder().config(config).build() + return analyzer.analyze().get().translationUnits + } + + /** + * Default way of parsing a list of files into a full CPG. All default passes are applied + * + * @param builder A [TranslationConfiguration.Builder] which contains the configuration + * @return A list of [TranslationUnitDeclaration] nodes, representing the CPG roots + * @throws Exception Any exception thrown during the parsing process + */ + @JvmStatic + @Throws(Exception::class) + fun analyzeWithBuilder( + builder: TranslationConfiguration.Builder + ): List { + val config = builder.build() + val analyzer = TranslationManager.builder().config(config).build() + return analyzer.analyze().get().translationUnits + } + + @JvmOverloads + @JvmStatic + @Throws(Exception::class) + fun analyzeAndGetFirstTU( + files: List?, + topLevel: Path, + usePasses: Boolean, + configModifier: Consumer? = null + ): TranslationUnitDeclaration { + val translationUnits = analyze(files, topLevel, usePasses, configModifier) + return translationUnits.stream().findFirst().orElseThrow() + } + + @JvmStatic + @Throws(IllegalAccessException::class) + fun disableTypeManagerCleanup() { + val spy = Mockito.spy(TypeManager.getInstance()) + Mockito.doNothing().`when`(spy).cleanup() + FieldUtils.writeStaticField(TypeManager::class.java, "INSTANCE", spy, true) + } + + /** + * Returns the first element of the specified Class-type `specificClass` that has the name + * `name` in the list `listOfNodes`. + * + * @param Some class that extends [Node]. + */ + @JvmStatic + fun getOfTypeWithName( + listOfNodes: List?, + specificClass: Class?, + name: String + ): S? { + val listOfNodesWithName = + Util.filterCast(listOfNodes, specificClass) + .stream() + .filter { s: S -> s!!.name == name } + .collect(Collectors.toList()) + return if (listOfNodesWithName.isEmpty()) { + null + } else listOfNodesWithName[0] + // Here we return the first node, if there are more nodes + } + + /** + * Returns the first element of the specified Class-type {code specifiedClass} that has the name + * `name` in the list of nodes that are subnodes of the AST-root node `root`. + * + * @param Some class that extends [Node]. + */ + @JvmStatic + fun getSubnodeOfTypeWithName( + root: Node?, + specificClass: Class?, + name: String + ): S? { + return getOfTypeWithName(SubgraphWalker.flattenAST(root), specificClass, name) + } + + /** + * Given a root node in the AST graph, all AST children of the node are filtered for a specific + * CPG Node type and returned. + * + * @param node root of the searched AST subtree + * @param specificClass Class type to be searched for + * @param Type variable that allows returning a list of specific type + * @return a List of searched types + */ + @JvmStatic + fun subnodesOfType(node: T, specificClass: Class): List { + return Util.filterCast(SubgraphWalker.flattenAST(node), specificClass) + .stream() + .filter(Util.distinctByIdentity()) + .collect(Collectors.toList()) + } + + /** + * Similar to [TestUtils.subnodesOfType] but used when working with a list of nodes that is + * already flat. No walking to get childnodes necessary. + */ + @JvmStatic + fun subnodesOfType(roots: Collection, specificClass: Class): List { + return roots + .stream() + .map { subnodesOfType(it, specificClass) } + .flatMap { it.stream() } + .filter(Util.distinctByIdentity()) + .collect(Collectors.toList()) + } + + /** + * Compare the given parameter `toCompare` to the start- or end-line of the given node. If the + * node has no location `false` is returned. `startLine` is used to specify if the start-line or + * end-line of a location is supposed to be used. + * + * @param n + * @param startLine + * @param toCompare + * @return + */ + @JvmStatic + fun compareLineFromLocationIfExists(n: Node, startLine: Boolean, toCompare: Int): Boolean { + val loc = n.location ?: return false + return if (startLine) { + loc.region.startLine == toCompare + } else { + loc.region.endLine == toCompare + } + } +} diff --git a/src/test/java/de/fraunhofer/aisec/cpg/enhancements/VisitorTest.java b/src/test/java/de/fraunhofer/aisec/cpg/enhancements/VisitorTest.java index a4c5119b9c..eca6acfdf4 100644 --- a/src/test/java/de/fraunhofer/aisec/cpg/enhancements/VisitorTest.java +++ b/src/test/java/de/fraunhofer/aisec/cpg/enhancements/VisitorTest.java @@ -61,7 +61,11 @@ public static void setup() throws TranslationException, InterruptedException, ExecutionException, TimeoutException { File file = new File("src/test/resources/compiling/RecordDeclaration.java"); TranslationConfiguration config = - TranslationConfiguration.builder().sourceLocations(file).defaultPasses().build(); + TranslationConfiguration.builder() + .sourceLocations(file) + .defaultPasses() + .defaultLanguages() + .build(); TranslationResult result = TranslationManager.builder().config(config).build().analyze().get(20, TimeUnit.SECONDS); TranslationUnitDeclaration tu = result.getTranslationUnits().get(0); diff --git a/src/test/java/de/fraunhofer/aisec/cpg/enhancements/calls/FunctionPointerTest.java b/src/test/java/de/fraunhofer/aisec/cpg/enhancements/calls/FunctionPointerTest.java index 8d16fc46e9..c242c8ead5 100644 --- a/src/test/java/de/fraunhofer/aisec/cpg/enhancements/calls/FunctionPointerTest.java +++ b/src/test/java/de/fraunhofer/aisec/cpg/enhancements/calls/FunctionPointerTest.java @@ -63,6 +63,7 @@ private List analyze(String language) throws Excepti .sourceLocations(files) .topLevel(topLevel.toFile()) .defaultPasses() + .defaultLanguages() .debugParser(true) .failOnError(true) .build(); diff --git a/src/test/java/de/fraunhofer/aisec/cpg/enhancements/variable_resolution/VariableResolverCppTest.java b/src/test/java/de/fraunhofer/aisec/cpg/enhancements/variable_resolution/VariableResolverCppTest.java index e81430e75d..ec04546529 100644 --- a/src/test/java/de/fraunhofer/aisec/cpg/enhancements/variable_resolution/VariableResolverCppTest.java +++ b/src/test/java/de/fraunhofer/aisec/cpg/enhancements/variable_resolution/VariableResolverCppTest.java @@ -83,6 +83,7 @@ void initTests() throws ExecutionException, InterruptedException { .topLevel(new File(topLevelPath)) .defaultPasses() .debugParser(true) + .defaultLanguages() .failOnError(true) .loadIncludes(true) .build(); diff --git a/src/test/java/de/fraunhofer/aisec/cpg/enhancements/variable_resolution/VariableResolverJavaTest.java b/src/test/java/de/fraunhofer/aisec/cpg/enhancements/variable_resolution/VariableResolverJavaTest.java index 53cf4b61ee..ea9cd2e6db 100644 --- a/src/test/java/de/fraunhofer/aisec/cpg/enhancements/variable_resolution/VariableResolverJavaTest.java +++ b/src/test/java/de/fraunhofer/aisec/cpg/enhancements/variable_resolution/VariableResolverJavaTest.java @@ -79,6 +79,7 @@ void initTests() throws ExecutionException, InterruptedException { .sourceLocations(fileLocations.toArray(new File[fileNames.size()])) .topLevel(new File(topLevelPath)) .defaultPasses() + .defaultLanguages() .debugParser(true) .failOnError(true) .build(); diff --git a/src/test/java/de/fraunhofer/aisec/cpg/frontends/LanguageFrontendTest.java b/src/test/java/de/fraunhofer/aisec/cpg/frontends/LanguageFrontendTest.java index ca52e939f4..aeeed46f3e 100644 --- a/src/test/java/de/fraunhofer/aisec/cpg/frontends/LanguageFrontendTest.java +++ b/src/test/java/de/fraunhofer/aisec/cpg/frontends/LanguageFrontendTest.java @@ -46,6 +46,7 @@ void testParseDirectory() throws ExecutionException, InterruptedException { TranslationConfiguration.builder() .sourceLocations(new File("src/test/resources/botan")) .debugParser(true) + .defaultLanguages() .build()) .build(); TranslationResult res = analyzer.analyze().get(); diff --git a/src/test/java/de/fraunhofer/aisec/cpg/frontends/cpp/CXXIncludeTest.java b/src/test/java/de/fraunhofer/aisec/cpg/frontends/cpp/CXXIncludeTest.java index e4a8c96b6f..5ba977cf3d 100644 --- a/src/test/java/de/fraunhofer/aisec/cpg/frontends/cpp/CXXIncludeTest.java +++ b/src/test/java/de/fraunhofer/aisec/cpg/frontends/cpp/CXXIncludeTest.java @@ -114,6 +114,7 @@ void testIncludeBlacklist() throws Exception { .topLevel(file.getParentFile()) .loadIncludes(true) .debugParser(true) + .defaultLanguages() .includeBlacklist(new File("src/test/resources/include.h").getAbsolutePath()) .failOnError(true)); @@ -142,6 +143,7 @@ void testIncludeBlacklistRelative() throws Exception { .topLevel(file.getParentFile()) .loadIncludes(true) .debugParser(true) + .defaultLanguages() .includeBlacklist("include.h") .failOnError(true)); @@ -170,6 +172,7 @@ void testIncludeWhitelist() throws Exception { .topLevel(file.getParentFile()) .loadIncludes(true) .debugParser(true) + .defaultLanguages() .includeWhitelist(new File("src/test/resources/include.h").getAbsolutePath()) .failOnError(true)); @@ -197,6 +200,7 @@ void testIncludeWhitelistRelative() throws Exception { .topLevel(file.getParentFile()) .loadIncludes(true) .debugParser(true) + .defaultLanguages() .includeWhitelist("include.h") .failOnError(true)); @@ -224,6 +228,7 @@ void testIncludeBothLists() throws Exception { .topLevel(file.getParentFile()) .loadIncludes(true) .debugParser(true) + .defaultLanguages() .includeBlacklist("include.h") // blacklist entries take priority .includeWhitelist("include.h") .includeWhitelist("another-include.h") @@ -256,6 +261,7 @@ void testLoadIncludes() throws Exception { .topLevel(file.getParentFile()) .loadIncludes(false) .debugParser(true) + .defaultLanguages() .failOnError(true)); assertNotNull(translationUnitDeclarations); diff --git a/src/test/java/de/fraunhofer/aisec/cpg/frontends/cpp/CXXLanguageFrontendTest.java b/src/test/java/de/fraunhofer/aisec/cpg/frontends/cpp/CXXLanguageFrontendTest.java index 99d85649ff..d7e8609872 100644 --- a/src/test/java/de/fraunhofer/aisec/cpg/frontends/cpp/CXXLanguageFrontendTest.java +++ b/src/test/java/de/fraunhofer/aisec/cpg/frontends/cpp/CXXLanguageFrontendTest.java @@ -1197,6 +1197,7 @@ void testAttributes() throws Exception { .sourceLocations(List.of(file)) .topLevel(file.getParentFile()) .defaultPasses() + .defaultLanguages() .processAnnotations(true) .symbols( Map.of("PROPERTY_ATTRIBUTE(...)", "[[property_attribute(#__VA_ARGS__)]]"))); @@ -1252,7 +1253,8 @@ void testUnityBuild() throws Exception { .topLevel(file.getParentFile()) .useUnityBuild(true) .loadIncludes(true) - .defaultPasses()); + .defaultPasses() + .defaultLanguages()); assertEquals(1, declarations.size()); diff --git a/src/test/java/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontendTest.kt b/src/test/java/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontendTest.kt index 487f682204..fbd271b0c1 100644 --- a/src/test/java/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontendTest.kt +++ b/src/test/java/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontendTest.kt @@ -1,6 +1,7 @@ package de.fraunhofer.aisec.cpg.frontends.golang import de.fraunhofer.aisec.cpg.BaseTest +import de.fraunhofer.aisec.cpg.ExperimentalGolang import de.fraunhofer.aisec.cpg.TestUtils import de.fraunhofer.aisec.cpg.graph.declarations.* import de.fraunhofer.aisec.cpg.graph.statements.* @@ -14,6 +15,7 @@ import org.junit.jupiter.api.Tag import org.junit.jupiter.api.Test @Tag("experimental") +@ExperimentalGolang class GoLanguageFrontendTest : BaseTest() { @Test @@ -24,7 +26,12 @@ class GoLanguageFrontendTest : BaseTest() { listOf(topLevel.resolve("construct.go").toFile()), topLevel, true - ) + ) { + it.registerLanguage( + GoLanguageFrontend::class.java, + GoLanguageFrontend.GOLANG_EXTENSIONS + ) + } assertNotNull(tu) @@ -129,7 +136,12 @@ class GoLanguageFrontendTest : BaseTest() { listOf(topLevel.resolve("literal.go").toFile()), topLevel, true - ) + ) { + it.registerLanguage( + GoLanguageFrontend::class.java, + GoLanguageFrontend.GOLANG_EXTENSIONS + ) + } assertNotNull(tu) @@ -166,7 +178,12 @@ class GoLanguageFrontendTest : BaseTest() { listOf(topLevel.resolve("function.go").toFile()), topLevel, true - ) + ) { + it.registerLanguage( + GoLanguageFrontend::class.java, + GoLanguageFrontend.GOLANG_EXTENSIONS + ) + } assertNotNull(tu) @@ -266,7 +283,12 @@ class GoLanguageFrontendTest : BaseTest() { listOf(topLevel.resolve("struct.go").toFile()), topLevel, true - ) + ) { + it.registerLanguage( + GoLanguageFrontend::class.java, + GoLanguageFrontend.GOLANG_EXTENSIONS + ) + } assertNotNull(tu) @@ -338,7 +360,12 @@ class GoLanguageFrontendTest : BaseTest() { listOf(topLevel.resolve("struct.go").toFile()), topLevel, true - ) + ) { + it.registerLanguage( + GoLanguageFrontend::class.java, + GoLanguageFrontend.GOLANG_EXTENSIONS + ) + } assertNotNull(tu) @@ -380,7 +407,12 @@ class GoLanguageFrontendTest : BaseTest() { listOf(topLevel.resolve("field.go").toFile()), topLevel, true - ) + ) { + it.registerLanguage( + GoLanguageFrontend::class.java, + GoLanguageFrontend.GOLANG_EXTENSIONS + ) + } assertNotNull(tu) @@ -420,7 +452,12 @@ class GoLanguageFrontendTest : BaseTest() { listOf(topLevel.resolve("if.go").toFile()), topLevel, true - ) + ) { + it.registerLanguage( + GoLanguageFrontend::class.java, + GoLanguageFrontend.GOLANG_EXTENSIONS + ) + } assertNotNull(tu) @@ -463,7 +500,12 @@ class GoLanguageFrontendTest : BaseTest() { listOf(topLevel.resolve("switch.go").toFile()), topLevel, true - ) + ) { + it.registerLanguage( + GoLanguageFrontend::class.java, + GoLanguageFrontend.GOLANG_EXTENSIONS + ) + } assertNotNull(tu) @@ -528,7 +570,12 @@ class GoLanguageFrontendTest : BaseTest() { ), topLevel, true - ) + ) { + it.registerLanguage( + GoLanguageFrontend::class.java, + GoLanguageFrontend.GOLANG_EXTENSIONS + ) + } assertNotNull(tus) diff --git a/src/test/java/de/fraunhofer/aisec/cpg/frontends/java/DemoTests.java b/src/test/java/de/fraunhofer/aisec/cpg/frontends/java/DemoTests.java index 582e747f5b..edb0a6f223 100644 --- a/src/test/java/de/fraunhofer/aisec/cpg/frontends/java/DemoTests.java +++ b/src/test/java/de/fraunhofer/aisec/cpg/frontends/java/DemoTests.java @@ -58,6 +58,7 @@ void testHierarchy() throws Exception { .sourceLocations(files) .topLevel(topLevel.toFile()) .defaultPasses() + .defaultLanguages() .debugParser(true) .failOnError(true) .build(); @@ -84,6 +85,7 @@ void testPartial() throws Exception { .sourceLocations(files) .topLevel(topLevel.toFile()) .defaultPasses() + .defaultLanguages() .debugParser(true) .failOnError(true) .build(); diff --git a/src/test/java/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguageFrontendTest.java b/src/test/java/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguageFrontendTest.java index 4c789e4b55..20c5a24ce0 100644 --- a/src/test/java/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguageFrontendTest.java +++ b/src/test/java/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguageFrontendTest.java @@ -527,6 +527,7 @@ void testAnnotations() throws Exception { .sourceLocations(List.of(file)) .topLevel(file.getParentFile()) .defaultPasses() + .defaultLanguages() .processAnnotations(true)); assertFalse(declarations.isEmpty()); diff --git a/src/test/java/de/fraunhofer/aisec/cpg/frontends/python/PythonFrontendTest.kt b/src/test/java/de/fraunhofer/aisec/cpg/frontends/python/PythonFrontendTest.kt index 25973e142e..ff44eeb461 100644 --- a/src/test/java/de/fraunhofer/aisec/cpg/frontends/python/PythonFrontendTest.kt +++ b/src/test/java/de/fraunhofer/aisec/cpg/frontends/python/PythonFrontendTest.kt @@ -31,7 +31,12 @@ class PythonFrontendTest : BaseTest() { listOf(topLevel.resolve("literal.py").toFile()), topLevel, true - ) + ) { + it.registerLanguage( + PythonLanguageFrontend::class.java, + PythonLanguageFrontend.PY_EXTENSIONS + ) + } assertNotNull(tu) @@ -81,7 +86,12 @@ class PythonFrontendTest : BaseTest() { listOf(topLevel.resolve("function.py").toFile()), topLevel, true - ) + ) { + it.registerLanguage( + PythonLanguageFrontend::class.java, + PythonLanguageFrontend.PY_EXTENSIONS + ) + } assertNotNull(tu) @@ -168,7 +178,12 @@ class PythonFrontendTest : BaseTest() { listOf(topLevel.resolve("if.py").toFile()), topLevel, true - ) + ) { + it.registerLanguage( + PythonLanguageFrontend::class.java, + PythonLanguageFrontend.PY_EXTENSIONS + ) + } assertNotNull(tu) @@ -208,7 +223,12 @@ class PythonFrontendTest : BaseTest() { listOf(topLevel.resolve("simple_class.py").toFile()), topLevel, true - ) + ) { + it.registerLanguage( + PythonLanguageFrontend::class.java, + PythonLanguageFrontend.PY_EXTENSIONS + ) + } assertNotNull(tu) val p = diff --git a/src/test/java/de/fraunhofer/aisec/cpg/graph/TranslationResultTest.kt b/src/test/java/de/fraunhofer/aisec/cpg/graph/TranslationResultTest.kt index 36d292b559..fffc702412 100644 --- a/src/test/java/de/fraunhofer/aisec/cpg/graph/TranslationResultTest.kt +++ b/src/test/java/de/fraunhofer/aisec/cpg/graph/TranslationResultTest.kt @@ -21,6 +21,7 @@ class TranslationResultTest : BaseTest() { .topLevel(file.parentFile) .defaultPasses() .debugParser(true) + .defaultLanguages() .failOnError(true) .build()