Skip to content

Commit

Permalink
Introduce Eclipse CDT parser, create SymbolTable
Browse files Browse the repository at this point in the history
* Use Eclipse CDT parser (incl. preprocessor) in order
  to create an AST

* Use it to retrieve the invormation about all symbols
  (declarations and references) of the compilation unit

patchset log:
* rebase and conflict resolving
* use preprocessor (include paths used, defines are not)
* filter macro expansions, since symbols are not placed in
  original source code
* update org.eclipse.cdt.core to 6.5.0 (photon), which
  claims to support c++14 & c++17 features
  • Loading branch information
ivangalkin committed Aug 26, 2018
1 parent bd5adde commit 6a98d3e
Show file tree
Hide file tree
Showing 19 changed files with 2,231 additions and 1 deletion.
80 changes: 80 additions & 0 deletions cxx-checks/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,84 @@
<scope>test</scope>
</dependency>
</dependencies>

<!-- Eclipse CDT is not tracked in maven repository -->
<!-- Workaround:
cxx-check/pom.xml
1. Download the latest jar (see also http://www.eclipse.org/downloads/download.php?file=/tools/cdt/releases/9.4/cdt-9.4.3/plugins/org.eclipse.cdt.core_6.4.0.201802261533.jar)
2. Install it to the local maven repository. This allows the proper version control for dependencies.
cxx-sensors/pom.xml (depends on cxx-check/pom.xml)
3. Use local repository in addition to the official maven repository
4. Add the dependency to Eclipse CDT
-->
<build>
<plugins>
<plugin>
<groupId>com.googlecode.maven-download-plugin</groupId>
<artifactId>download-maven-plugin</artifactId>
<version>1.4.0</version>
<executions>
<execution>
<id>download-eclipse-cdt-core</id>
<phase>generate-sources</phase>
<goals>
<goal>wget</goal>
</goals>
<configuration>
<url>https://ftp.halifax.rwth-aachen.de/eclipse/tools/cdt/releases/9.5/cdt-9.5.2/plugins/org.eclipse.cdt.core_6.5.0.201807181141.jar</url>
<outputFileName>org.eclipse.cdt.core_6.5.0.201807181141.jar</outputFileName>
<outputDirectory>${java.io.tmpdir}</outputDirectory>
<md5>9fc8edd950a527149ae33e79bc858c91</md5>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.googlecode.maven-download-plugin</groupId>
<artifactId>download-maven-plugin</artifactId>
<version>1.4.0</version>
<executions>
<execution>
<id>download-eclipse-cdt-core-sources</id>
<phase>generate-sources</phase>
<goals>
<goal>wget</goal>
</goals>
<configuration>
<url>https://ftp.halifax.rwth-aachen.de/eclipse/tools/cdt/releases/9.5/cdt-9.5.2/plugins/org.eclipse.cdt.core.source_6.5.0.201807181141.jar</url>
<outputFileName>org.eclipse.cdt.core.source_6.5.0.201807181141.jar</outputFileName>
<outputDirectory>${java.io.tmpdir}</outputDirectory>
<md5>9ac89ce26712bf0b078aa178315daa15</md5>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.1</version>
<configuration>
<groupId>org.eclipse.cdt</groupId>
<artifactId>org.eclipse.cdt.core</artifactId>
<version>6.5.0</version>
<packaging>jar</packaging>
<file>${java.io.tmpdir}/org.eclipse.cdt.core_6.5.0.201807181141.jar</file>
<sources>${java.io.tmpdir}/org.eclipse.cdt.core.source_6.5.0.201807181141.jar</sources>
<generatePom>true</generatePom>
<localRepositoryPath>${java.io.tmpdir}/sonar-cxx-local-maven/</localRepositoryPath>
<createChecksum>true</createChecksum>
</configuration>
<executions>
<execution>
<id>install-eclipse-cdt-core</id>
<goals>
<goal>install-file</goal>
</goals>
<phase>generate-sources</phase>
</execution>
</executions>
</plugin>
</plugins>
</build>

</project>
24 changes: 24 additions & 0 deletions cxx-sensors/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@
<sonar.artifact.path>target/${project.artifactId}-${project.version}.jar</sonar.artifact.path>
</properties>

<!-- see cxx-checks/pom.xml -->
<repositories>
<repository>
<id>sonar-cxx-local-maven</id>
<url>file://${java.io.tmpdir}/sonar-cxx-local-maven/</url>
</repository>
</repositories>

<dependencies>
<dependency>
<groupId>org.sonarsource.sonarqube</groupId>
Expand Down Expand Up @@ -100,5 +108,21 @@
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.eclipse.cdt</groupId>
<artifactId>org.eclipse.cdt.core</artifactId>
<version>6.5.0</version>
</dependency>
<dependency>
<groupId>org.eclipse.platform</groupId>
<artifactId>org.eclipse.core.runtime</artifactId>
<version>3.14.0</version>
</dependency>
<dependency>
<groupId>com.ibm.icu</groupId>
<artifactId>icu4j</artifactId>
<version>3.4.4</version>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Sonar C++ Plugin (Community)
* Copyright (C) 2010-2018 SonarOpenCommunity
* http://github.com/SonarOpenCommunity/sonar-cxx
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.cxx.sensors.eclipsecdt;

public class EclipseCDTException extends Exception {

private static final long serialVersionUID = 4069232895507964456L;

EclipseCDTException(String message) {
super(message);
}

public EclipseCDTException(String message, Throwable e) {
super(message, e);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
/*
* Sonar C++ Plugin (Community)
* Copyright (C) 2010-2018 SonarOpenCommunity
* http://github.com/SonarOpenCommunity/sonar-cxx
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.cxx.sensors.eclipsecdt;

import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.apache.commons.io.FileUtils;
import org.eclipse.cdt.core.dom.ast.ASTGenericVisitor;
import org.eclipse.cdt.core.dom.ast.IASTDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTFileLocation;
import org.eclipse.cdt.core.dom.ast.IASTImageLocation;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTProblem;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.gnu.cpp.GPPLanguage;
import org.eclipse.cdt.core.index.IIndex;
import org.eclipse.cdt.core.index.IIndexFileLocation;
import org.eclipse.cdt.core.parser.FileContent;
import org.eclipse.cdt.core.parser.IParserLogService;
import org.eclipse.cdt.core.parser.IScannerInfo;
import org.eclipse.cdt.core.parser.IncludeFileContentProvider;
import org.eclipse.cdt.core.parser.ScannerInfo;
import org.eclipse.cdt.core.parser.util.CharArrayUtils;
import org.eclipse.cdt.internal.core.parser.IMacroDictionary;
import org.eclipse.cdt.internal.core.parser.scanner.InternalFileContent;
import org.eclipse.cdt.internal.core.parser.scanner.InternalFileContentProvider;
import org.eclipse.core.runtime.CoreException;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;

public class EclipseCDTParser {

private static final Logger LOG = Loggers.get(EclipseCDTParser.class);

/**
* Naive implementation for IncludeFileContentProvider, see
* {@link org.eclipse.cdt.internal.core.parser.EmptyFilesProvider}
*
* InernalFile is a source file from the eclipse workspace. Usage of
* FileContent.createForExternalFileLocation() seems to be inappropropriate.
*/
private static final IncludeFileContentProvider INCLUDE_FILE_PROVIDER = new InternalFileContentProvider() {
private InternalFileContent getContentUncached(String path) {
if (!getInclusionExists(path)) {
return null;
}

char[] contents = CharArrayUtils.EMPTY;
try {
contents = FileUtils.readFileToString(new File(path), Charset.defaultCharset()).toCharArray();
} catch (IOException e) {
LOG.debug("EclipseCDTParser: Unable to read the content of {}", path);
}
return (InternalFileContent) FileContent.create(path, contents);
}

@Override
public InternalFileContent getContentForInclusion(String path, IMacroDictionary macroDictionary) {
return getContentUncached(path);
}

@Override
public InternalFileContent getContentForInclusion(IIndexFileLocation ifl, String astPath) {
return getContentUncached(astPath);
}
};

private static final IParserLogService LOG_ADAPTER = new IParserLogService() {
@Override
public boolean isTracing() {
return LOG.isDebugEnabled();
}

@Override
public void traceLog(String msg) {
LOG.debug(msg);
}
};

private final String sourcePath;
private final IASTTranslationUnit translationUnit;
private final LinebasedOffsetTranslator offsetTranslator;

public EclipseCDTParser(String path, String[] includePaths) throws EclipseCDTException {
sourcePath = path;
try {
offsetTranslator = new LinebasedOffsetTranslator(path);
} catch (IOException e) {
throw new EclipseCDTException("Unable to read file " + path, e);
}

final IIndex ignoreIndex = null;
final Map<String, String> ignoreDefinedMacros = null;
final int noSpecialParseOptions = 0;
IScannerInfo scannerInfo = new ScannerInfo(ignoreDefinedMacros, includePaths);
FileContent fileContent = FileContent.createForExternalFileLocation(path);

try {
translationUnit = GPPLanguage.getDefault().getASTTranslationUnit(fileContent, scannerInfo, INCLUDE_FILE_PROVIDER,
ignoreIndex, noSpecialParseOptions, LOG_ADAPTER);
} catch (CoreException e) {
throw new EclipseCDTException("Unable to parse file " + path, e);
}

logPreprocessorProblems();
}

private void logPreprocessorProblems() {
if (LOG.isDebugEnabled()) {
for (IASTProblem problem : translationUnit.getPreprocessorProblems()) {
LOG.debug(problem.getMessageWithLocation());
}
}
}

/**
* Traverse the given top-level declaration and find all IASTName nodes, which
* describe this or arbitrary nested declaration
*/
private Set<IASTName> getDeclarationNameNodes(IASTDeclaration declaration) {
final Set<IASTName> declarationNameNodes = new HashSet<>();
declaration.accept(new ASTGenericVisitor(true) {
{
includeInactiveNodes = true;
}

@Override
public int visit(IASTName name) {
if (name.isDeclaration()) {
declarationNameNodes.add(name);
}
return PROCESS_CONTINUE;
}

});

return declarationNameNodes;
}

private LinebasedTextRange newRange(IASTFileLocation location) throws EclipseCDTException {
int globalOffset = location.getNodeOffset();
int length = location.getNodeLength();
LinebasedTextPointer start = offsetTranslator.newPointer(globalOffset);
LinebasedTextPointer end = offsetTranslator.newPointer(globalOffset + length);
return new LinebasedTextRange(start, end);
}

private LinebasedTextRange newRange(IASTName astName) throws EclipseCDTException {
try {
return newRange(astName.getFileLocation());
} catch (EclipseCDTException e) {
throw new EclipseCDTException("Unable to create LinebasedTextRange for symbol [name=" + astName + ", location="
+ astName.getFileLocation() + ", file=" + sourcePath + ", error=" + e.getMessage() + "]", e);
}
}

private Boolean isLocatedInFile(IASTName name) {
if (name == null || !name.isPartOfTranslationUnitFile()) {
return false;
}
IASTFileLocation fileLocation = name.getFileLocation();
return fileLocation != null && sourcePath.equals(fileLocation.getFileName()) && fileLocation.getNodeLength() > 0;
}

private Boolean isPartOfSymbolTable(IASTName name) {
if (!isLocatedInFile(name)) {
return false;
}
IASTImageLocation imageLocation = name.getImageLocation();
return (imageLocation != null) && (imageLocation.getLocationKind() == IASTImageLocation.REGULAR_CODE);
}

public Map<LinebasedTextRange, Set<LinebasedTextRange>> generateSymbolTable() throws EclipseCDTException {
// collect all declarations from the translation unit
IASTDeclaration[] declarations = translationUnit.getDeclarations(true);
final Set<IASTName> declarationNames = new HashSet<>();
for (IASTDeclaration declaration : declarations) {
declarationNames.addAll(getDeclarationNameNodes(declaration));
}

// collect all references to the declarations
Map<LinebasedTextRange, Set<LinebasedTextRange>> table = new HashMap<>();
for (IASTName declarationName : declarationNames) {
IBinding binding = declarationName.resolveBinding();
if (binding == null) {
continue;
}

Set<LinebasedTextRange> references = new HashSet<>();
for (IASTName referenceName : translationUnit.getReferences(binding)) {
if (referenceName != null && isPartOfSymbolTable(referenceName)) {
references.add(newRange(referenceName));
}
}

if (isPartOfSymbolTable(declarationName)) {
table.put(newRange(declarationName), references);
} else if (!references.isEmpty()) {
LinebasedTextRange randomReference = references.iterator().next();
references.remove(randomReference);
table.put(randomReference, references);
} else {
continue;
}

}
return table;
}
}
Loading

0 comments on commit 6a98d3e

Please sign in to comment.