Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
cmake_minimum_required(VERSION 3.14)
project(armor)
set(TOOL_VERSION "0.6.5")

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
Expand All @@ -11,13 +12,19 @@ execute_process(
OUTPUT_STRIP_TRAILING_WHITESPACE
)

set(CLANG_EXTRA_FLAGS
set(CLANG_EXTRA_FLAGS_CPP
"-std=c++17 -xc++ -isystem /usr/include/c++/11 -isystem /usr/include -isystem /usr/local/include -isystem /usr/include/x86_64-linux-gnu/c++/11 -I/usr/lib/llvm-14/include -I/usr/lib/clang/14/include"
)

string(REPLACE "\"" "\\\"" CLANG_EXTRA_FLAGS_ESCAPED "${CLANG_EXTRA_FLAGS}")
set(CLANG_EXTRA_FLAGS_C
"-xc -isystem /usr/include -isystem /usr/local/include -isystem /usr/include/x86_64-linux-gnu -I/usr/lib/llvm-14/include -I/usr/lib/clang/14/include"
)

string(REPLACE "\"" "\\\"" CLANG_EXTRA_FLAGS_CPP_ESCAPED "${CLANG_EXTRA_FLAGS_CPP}")
string(REPLACE "\"" "\\\"" CLANG_EXTRA_FLAGS_C_ESCAPED "${CLANG_EXTRA_FLAGS_C}")

add_compile_definitions(CLANG_FLAGS="${CLANG_EXTRA_FLAGS_ESCAPED}")
add_compile_definitions(CLANG_FLAGS_CPP="${CLANG_EXTRA_FLAGS_CPP_ESCAPED}")
add_compile_definitions(CLANG_FLAGS_C="${CLANG_EXTRA_FLAGS_C_ESCAPED}")

find_package(LLVM REQUIRED CONFIG)
find_package(Clang REQUIRED CONFIG)
Expand All @@ -38,7 +45,7 @@ llvm_map_components_to_libnames(LLVM_LIBS
option
)

add_definitions(-DTOOL_VERSION="0.6.3")
add_definitions(-DTOOL_VERSION="${TOOL_VERSION}")

add_subdirectory(src/common)
add_subdirectory(src/alpha)
Expand Down
14 changes: 13 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ The tool is invoked directly using the `armor` binary:
Report format: `html` (default).
If `json` is provided, both HTML and JSON reports will be generated.

* **-l, --lang TEXT:{c,cpp}**
Language mode: `cpp` (default) or `c`.
Use `c` for C headers, `cpp` for C++ headers.

* **--dump-ast-diff**
Dump AST diff JSON files for debugging

Expand Down Expand Up @@ -136,7 +140,7 @@ The tool is invoked directly using the `armor` binary:

2. **Comparison with relative header paths:**
```bash
./build/src/armor/armor /path/to/old/project /path/to/new/project include/api/foo.h include/api/bar.h
./build/src/armor/armor /path/to/old/project /path/to/new/project include/api/foo.h
```

3. **Generate JSON report with include paths:**
Expand All @@ -159,6 +163,14 @@ The tool is invoked directly using the `armor` binary:
foo.h
```

5. **Comparing C headers:**
```bash
./build/src/armor/armor /path/to/old/project /path/to/new/project \
--header-dir include \
--lang c \
foo.h
```

Test suite
----------

Expand Down
2 changes: 2 additions & 0 deletions src/alpha/include/astnormalizer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class ASTNormalize : public clang::RecursiveASTVisitor<ASTNormalize> {
ASTNormalize(alpha::APISession* session, alpha::ASTNormalizedContext* context, clang::ASTContext* clangContext);

bool TraverseNamespaceDecl(clang::NamespaceDecl *Decl);
bool TraverseRecordDecl(clang::RecordDecl *Decl);
bool TraverseCXXRecordDecl(clang::CXXRecordDecl *Decl);
bool TraverseCXXConstructorDecl(clang::CXXConstructorDecl *Decl);
bool TraverseCXXMethodDecl(clang::CXXMethodDecl *Decl);
Expand All @@ -40,6 +41,7 @@ class ASTNormalize : public clang::RecursiveASTVisitor<ASTNormalize> {
bool TraverseClassTemplatePartialSpecializationDecl(clang::ClassTemplatePartialSpecializationDecl *Decl);

bool VisitNamespaceDecl(clang::NamespaceDecl *Decl);
bool VisitRecordDecl(clang::RecordDecl *Decl);
bool VisitCXXRecordDecl(clang::CXXRecordDecl *Decl);
bool VisitEnumDecl(clang::EnumDecl *Decl);
bool VisitFieldDecl(clang::FieldDecl *Decl);
Expand Down
4 changes: 3 additions & 1 deletion src/alpha/include/header_processor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include <string>
#include <vector>
#include "comm_def.hpp"
#include "session.hpp"

PARSING_STATUS processHeaderPairAlpha(const std::string& projectRoot1,
Expand All @@ -12,4 +13,5 @@ PARSING_STATUS processHeaderPairAlpha(const std::string& projectRoot1,
const std::string& file2,
const std::string& reportFormat,
const std::vector<std::string>& IncludePaths,
const std::vector<std::string>& macroFlags);
const std::vector<std::string>& macroFlags,
const LANG_OPTIONS lang);
1 change: 1 addition & 0 deletions src/alpha/include/tree_builder.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ class TreeBuilder {
void normalizeValueDeclNode(const clang::ValueDecl *Decl, unsigned int pos = -1);

// Node building methods
bool BuildRecordNode(clang::RecordDecl* Decl);
bool BuildCXXRecordNode(clang::CXXRecordDecl* Decl);
bool BuildEnumNode(clang::EnumDecl* Decl);
bool BuildFunctionNode(clang::FunctionDecl* Decl);
Expand Down
26 changes: 26 additions & 0 deletions src/alpha/src/astnormalizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,26 @@ bool alpha::ASTNormalize::TraverseNamespaceDecl(clang::NamespaceDecl *Decl) {
return true;
}

bool alpha::ASTNormalize::TraverseRecordDecl(clang::RecordDecl *Decl) {
// Only handle pure C RecordDecl β€” CXXRecordDecl is a subclass and has its own Traverse
if (llvm::isa<clang::CXXRecordDecl>(Decl)) {
return RecursiveASTVisitor<alpha::ASTNormalize>::TraverseRecordDecl(Decl);
}

RecursiveASTVisitor<alpha::ASTNormalize>::TraverseRecordDecl(Decl);

llvm::SmallString<128> nameBuf;
llvm::raw_svector_ostream OS(nameBuf);
Decl->printName(OS);

if (treeBuilder.IsFromMainFileAndNotLocal(Decl) && Decl->isThisDeclarationADefinition() && !nameBuf.empty()) {
treeBuilder.PopName();
treeBuilder.PopNode();
}

return true;
}

bool alpha::ASTNormalize::TraverseCXXRecordDecl(clang::CXXRecordDecl *Decl) {

RecursiveASTVisitor<alpha::ASTNormalize>::TraverseCXXRecordDecl(Decl);
Expand Down Expand Up @@ -146,6 +166,12 @@ bool alpha::ASTNormalize::VisitNamespaceDecl(clang::NamespaceDecl *Decl) {
return false;
}

bool alpha::ASTNormalize::VisitRecordDecl(clang::RecordDecl *Decl) {
// Only handle pure C RecordDecl β€” skip if this is actually a CXXRecordDecl
if (llvm::isa<clang::CXXRecordDecl>(Decl)) return true;
return treeBuilder.BuildRecordNode(Decl);
}

bool alpha::ASTNormalize::VisitCXXRecordDecl(clang::CXXRecordDecl *Decl) {
return treeBuilder.BuildCXXRecordNode(Decl);
}
Expand Down
29 changes: 20 additions & 9 deletions src/alpha/src/header_processor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,27 @@ using namespace clang;
using namespace clang::tooling;
using namespace llvm;

#ifndef CLANG_FLAGS
#define CLANG_FLAGS ""
#endif

namespace {

std::vector<std::string> getClangFlags(const std::vector<std::string>& includePaths,
const std::vector<std::string>& macroFlags) {
const std::vector<std::string>& macroFlags,
const LANG_OPTIONS lang) {
std::vector<std::string> flags;

std::istringstream iss(CLANG_FLAGS);
const char* rawFlags;
switch (lang) {
case LANG_OPTIONS::C:
rawFlags = CLANG_FLAGS_C;
break;
case LANG_OPTIONS::CPP:
rawFlags = CLANG_FLAGS_CPP;
break;
default:
rawFlags = CLANG_FLAGS_CPP;
break;
}

std::istringstream iss(rawFlags);
std::string flag;
while (iss >> flag) {
flags.emplace_back(std::move(flag));
Expand Down Expand Up @@ -118,7 +128,8 @@ PARSING_STATUS processHeaderPairAlpha(const std::string& project1,
const std::string& file2,
const std::string& reportFormat,
const std::vector<std::string>& IncludePaths,
const std::vector<std::string>& macroFlags) {
const std::vector<std::string>& macroFlags,
const LANG_OPTIONS lang) {

if (!DebugConfig::getInstance().initialize()) {
armor::user_error() << "Failed to open diagnostics log <" << LOG_FILE_PATH << ">, using stderr\n";
Expand All @@ -130,8 +141,8 @@ PARSING_STATUS processHeaderPairAlpha(const std::string& project1,
std::vector<std::string> inclusionPaths1 = resolveInternalIncludePaths(IncludePaths, project1);
std::vector<std::string> inclusionPaths2 = resolveInternalIncludePaths(IncludePaths, project2);

std::vector<std::string> Flags1 = getClangFlags(inclusionPaths1, macroFlags);
std::vector<std::string> Flags2 = getClangFlags(inclusionPaths2, macroFlags);
std::vector<std::string> Flags1 = getClangFlags(inclusionPaths1, macroFlags, lang);
std::vector<std::string> Flags2 = getClangFlags(inclusionPaths2, macroFlags, lang);

Flags1.insert(Flags1.end(), inclusion_paths1.begin(), inclusion_paths1.end());
Flags2.insert(Flags2.end(), inclusion_paths2.begin(), inclusion_paths2.end());
Expand Down
7 changes: 5 additions & 2 deletions src/alpha/src/preprocessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "clang/AST/ASTContext.h"
#include "clang/Basic/SourceManager.h"
#include <cassert>
#include <llvm-14/llvm/ADT/StringRef.h>
#include <llvm-14/llvm/Support/FileSystem.h>
#include <llvm-14/llvm/Support/Path.h>
#include <llvm-14/llvm/Support/raw_ostream.h>
Expand Down Expand Up @@ -34,8 +35,10 @@ void ASTNormalizerPreprocessor::InclusionDirective(
if (HashLoc.isValid()) {
clang::PresumedLoc PLoc = SM->getPresumedLoc(HashLoc);
if (PLoc.isValid()) {
armor::debug() << "Failed include - RelativePath: " << RelativePath << " at " << PLoc.getFilename() << "\n";
context->getSourceRangeTracker().addFatalDirective(RelativePath,PLoc.getFilename());
if(llvm::StringRef fileName = PLoc.getFilename(); !fileName.empty() && !RelativePath.empty()){
armor::debug() << "Failed include - RelativePath: " << RelativePath << " at " << PLoc.getFilename() << "\n";
context->getSourceRangeTracker().addFatalDirective(RelativePath,PLoc.getFilename());
}
}
}

Expand Down
42 changes: 42 additions & 0 deletions src/alpha/src/tree_builder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,48 @@ void alpha::TreeBuilder::normalizeValueDeclNode(const clang::ValueDecl *Decl, un
PopName();
}

bool alpha::TreeBuilder::BuildRecordNode(clang::RecordDecl* Decl) {

// Building RecordDecl for C specifaically
if (llvm::isa<clang::CXXRecordDecl>(Decl)) return true;

llvm::SmallString<128> nameBuf;
llvm::raw_svector_ostream OS(nameBuf);
Decl->printName(OS);

if (!IsFromMainFileAndNotLocal(Decl) || !Decl->isThisDeclarationADefinition() || nameBuf.empty()) {
return false;
}

if (Decl->getTypedefNameForAnonDecl()) {
return false;
} else {
if (Decl->hasNameForLinkage()) {
PushName(nameBuf);
}
}

const std::string qualifiedName = GetCurrentQualifiedName();
auto recordNode = std::make_shared<APINode>();

recordNode->qualifiedName = qualifiedName;

armor::debug() << "VisitRecordDecl (C): " << qualifiedName << "\n";

if (Decl->isStruct()) {
recordNode->kind = NodeKind::Struct;
recordNode->hash = generateHash(qualifiedName, NodeKind::Struct);
} else if (Decl->isUnion()) {
recordNode->kind = NodeKind::Union;
recordNode->hash = generateHash(qualifiedName, NodeKind::Union);
}

AddNode(recordNode);
PushNode(recordNode);

return true;
}

bool alpha::TreeBuilder::BuildCXXRecordNode(clang::CXXRecordDecl* Decl) {

llvm::SmallString<128> nameBuf;
Expand Down
Loading
Loading