Skip to content

Commit

Permalink
Generate declarations for unnamed fields of structs and unions (#604)
Browse files Browse the repository at this point in the history
* Generate declarations for unnamed fields of structs and unions
  • Loading branch information
IDKWNTCMF authored Jul 26, 2023
1 parent 83a81ec commit 593344f
Show file tree
Hide file tree
Showing 16 changed files with 268 additions and 27 deletions.
4 changes: 2 additions & 2 deletions server/src/Server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -203,10 +203,10 @@ Status Server::TestsGenServiceImpl::ProcessBaseTestRequest(BaseTestGen &testGen,
&sizeContext.maximumAlignment,
testGen.compileCommandsJsonPath, false);
fetcher.fetchWithProgress(testGen.progressWriter, logMessage);
types::TypesHandler typesHandler{testGen.types, sizeContext};
SourceToHeaderRewriter(testGen.projectContext, testGen.getTargetBuildDatabase()->compilationDatabase,
fetcher.getStructsToDeclare(), testGen.serverBuildDir)
fetcher.getStructsToDeclare(), testGen.serverBuildDir, typesHandler)
.generateTestHeaders(testGen.tests, testGen.progressWriter);
types::TypesHandler typesHandler{testGen.types, sizeContext};
testGen.progressWriter->writeProgress("Generating stub files", 0.0);
StubGen stubGen(testGen);

Expand Down
11 changes: 6 additions & 5 deletions server/src/Synchronizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ void Synchronizer::synchronize(const types::TypesHandler &typesHandler) {
synchronizeStubs(outdatedStubs, typesHandler);
}
auto outdatedSourcePaths = getOutdatedSourcePaths();
synchronizeWrappers(outdatedSourcePaths);
synchronizeWrappers(outdatedSourcePaths, typesHandler);
}

void Synchronizer::synchronizeStubs(StubSet &outdatedStubs,
Expand Down Expand Up @@ -191,7 +191,7 @@ void Synchronizer::synchronizeStubs(StubSet &outdatedStubs,

auto sourceToHeaderRewriter =
SourceToHeaderRewriter(testGen->projectContext, testGen->getProjectBuildDatabase()->compilationDatabase,
stubFetcher.getStructsToDeclare(), testGen->serverBuildDir);
stubFetcher.getStructsToDeclare(), testGen->serverBuildDir, typesHandler);

for (const StubOperator &outdatedStub : outdatedStubs) {
fs::path stubPath = outdatedStub.getStubPath(testGen->projectContext);
Expand Down Expand Up @@ -221,7 +221,8 @@ Synchronizer::createStubsCompilationDatabase(StubSet &stubFiles,
return CompilationUtils::getCompilationDatabase(ccJsonStubDirPath);
}

void Synchronizer::synchronizeWrappers(const CollectionUtils::FileSet &outdatedSourcePaths) const {
void Synchronizer::synchronizeWrappers(const CollectionUtils::FileSet &outdatedSourcePaths,
const types::TypesHandler &typesHandler) const {
auto sourceFilesNeedToRegenerateWrappers = outdatedSourcePaths;
for (fs::path const &sourceFilePath : getTargetSourceFiles()) {
if (!CollectionUtils::contains(sourceFilesNeedToRegenerateWrappers, sourceFilePath)) {
Expand All @@ -234,10 +235,10 @@ void Synchronizer::synchronizeWrappers(const CollectionUtils::FileSet &outdatedS
}
ExecUtils::doWorkWithProgress(
sourceFilesNeedToRegenerateWrappers, testGen->progressWriter,
"Generating wrappers", [this](fs::path const &sourceFilePath) {
"Generating wrappers", [this, &typesHandler](fs::path const &sourceFilePath) {
SourceToHeaderRewriter sourceToHeaderRewriter(testGen->projectContext,
testGen->getProjectBuildDatabase()->compilationDatabase, nullptr,
testGen->serverBuildDir);
testGen->serverBuildDir, typesHandler);
std::string wrapper = sourceToHeaderRewriter.generateWrapper(sourceFilePath);
printer::SourceWrapperPrinter(Paths::getSourceLanguage(sourceFilePath)).print(testGen->projectContext, sourceFilePath, wrapper);
});
Expand Down
3 changes: 2 additions & 1 deletion server/src/Synchronizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ class Synchronizer {

void synchronizeStubs(std::unordered_set<StubOperator, HashUtils::StubHash> &outdatedStubs,
const types::TypesHandler &typesHandler);
void synchronizeWrappers(const CollectionUtils::FileSet &outdatedSourcePaths) const;
void synchronizeWrappers(const CollectionUtils::FileSet &outdatedSourcePaths,
const types::TypesHandler &typesHandler) const;

std::shared_ptr<CompilationDatabase>
createStubsCompilationDatabase(
Expand Down
47 changes: 46 additions & 1 deletion server/src/clang-utils/SourceToHeaderMatchCallback.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,14 @@ SourceToHeaderMatchCallback::SourceToHeaderMatchCallback(utbot::ProjectContext p
fs::path sourceFilePath,
raw_ostream *externalStream,
raw_ostream *internalStream,
raw_ostream *unnamedTypeDeclsStream,
raw_ostream *wrapperStream,
const types::TypesHandler &typesHandler,
bool forStubHeader)
: projectContext(std::move(projectContext)),
sourceFilePath(std::move(sourceFilePath)), externalStream(externalStream),
internalStream(internalStream), wrapperStream(wrapperStream), forStubHeader(forStubHeader) {
internalStream(internalStream), unnamedTypeDeclsStream(unnamedTypeDeclsStream),
wrapperStream(wrapperStream), typesHandler(typesHandler), forStubHeader(forStubHeader) {
}

void SourceToHeaderMatchCallback::run(const ast_matchers::MatchFinder::MatchResult &Result) {
Expand Down Expand Up @@ -128,12 +131,14 @@ void SourceToHeaderMatchCallback::checkVarDecl(const MatchFinder::MatchResult &R

void SourceToHeaderMatchCallback::handleStruct(const RecordDecl *decl) {
print(decl);
generateUnnamedTypeDecls(decl);
}
void SourceToHeaderMatchCallback::handleEnum(const EnumDecl *decl) {
print(decl);
}
void SourceToHeaderMatchCallback::handleUnion(const RecordDecl *decl) {
print(decl);
generateUnnamedTypeDecls(decl);
}

void SourceToHeaderMatchCallback::handleTypedef(const TypedefDecl *decl) {
Expand Down Expand Up @@ -301,6 +306,36 @@ void SourceToHeaderMatchCallback::generateWrapper(const VarDecl *decl) const {
*wrapperStream << wrapperPointerDecl << " = &" << name << ";\n";
}

void SourceToHeaderMatchCallback::generateUnnamedTypeDecls(const clang::RecordDecl *decl) const {
if (unnamedTypeDeclsStream == nullptr) {
return;
}
clang::ASTContext const &context = decl->getASTContext();
clang::QualType canonicalType = context.getTypeDeclType(decl).getCanonicalType();
uint64_t id = types::Type::getIdFromCanonicalType(canonicalType);
if (typesHandler.isStructLike(id)) {
types::StructInfo info = typesHandler.getStructInfo(id);
generateUnnamedTypeDeclsForFields(info);
}
}

void SourceToHeaderMatchCallback::generateUnnamedTypeDeclsForFields(const types::StructInfo &info) const {
for (const types::Field &field : info.fields) {
if (!field.unnamedType || field.anonymous) {
continue;
}
if (typesHandler.isStructLike(field.type)) {
types::StructInfo fieldInfo = typesHandler.getStructInfo(field.type);
printUnnamedTypeDecl(info.name, field.name, fieldInfo.name);
generateUnnamedTypeDeclsForFields(fieldInfo);
}
if (typesHandler.isEnum(field.type)) {
types::EnumInfo enumInfo = typesHandler.getEnumInfo(field.type);
printUnnamedTypeDecl(info.name, field.name, enumInfo.name);
}
}
}


void SourceToHeaderMatchCallback::printReturn(const FunctionDecl *decl,
std::string const &name,
Expand All @@ -320,6 +355,16 @@ void SourceToHeaderMatchCallback::printReturn(const FunctionDecl *decl,
*stream << printer.ss.str();
}

void SourceToHeaderMatchCallback::printUnnamedTypeDecl(const std::string &structName,
const std::string &fieldName,
const std::string &typeName) const {
std::string typeDecl = StringUtils::stringFormat(
"typedef decltype(%s::%s) %s;\n",
structName, fieldName, typeName
);
*unnamedTypeDeclsStream << typeDecl;
}

std::string SourceToHeaderMatchCallback::decorate(std::string_view name) const {
return forStubHeader ? std::string(name) : NameDecorator::decorate(name);
}
Expand Down
13 changes: 13 additions & 0 deletions server/src/clang-utils/SourceToHeaderMatchCallback.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,23 @@ class SourceToHeaderMatchCallback : public clang::ast_matchers::MatchFinder::Mat
fs::path sourceFilePath;
llvm::raw_ostream *const externalStream = nullptr;
llvm::raw_ostream *const internalStream = nullptr;
llvm::raw_ostream *const unnamedTypeDeclsStream = nullptr;
llvm::raw_ostream *const wrapperStream = nullptr;

std::unordered_set<std::string> variables{};

const types::TypesHandler &typesHandler;

bool forStubHeader;
public:
SourceToHeaderMatchCallback(
utbot::ProjectContext projectContext,
fs::path sourceFilePath,
llvm::raw_ostream *externalStream,
llvm::raw_ostream *internalStream,
llvm::raw_ostream *unnamedTypeDeclsStream,
llvm::raw_ostream *wrapperStream,
const types::TypesHandler &typesHandler,
bool forStubHeader);

void run(const MatchFinder::MatchResult &Result) override;
Expand Down Expand Up @@ -72,6 +77,10 @@ class SourceToHeaderMatchCallback : public clang::ast_matchers::MatchFinder::Mat
std::string const &name,
llvm::raw_ostream *stream) const;

void printUnnamedTypeDecl(const std::string &structName,
const std::string &fieldName,
const std::string &typeName) const;

void generateWrapper(const clang::FunctionDecl *decl) const;

void generateWrapper(const clang::VarDecl *decl) const;
Expand All @@ -80,6 +89,10 @@ class SourceToHeaderMatchCallback : public clang::ast_matchers::MatchFinder::Mat

void generateInternal(const clang::VarDecl *decl) const;

void generateUnnamedTypeDeclsForFields(const types::StructInfo &info) const;

void generateUnnamedTypeDecls(const clang::RecordDecl *decl) const;

std::string getRenamedDeclarationAsString(const clang::NamedDecl *decl,
clang::PrintingPolicy const &policy,
std::string const &name) const;
Expand Down
37 changes: 24 additions & 13 deletions server/src/clang-utils/SourceToHeaderRewriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,26 @@ SourceToHeaderRewriter::SourceToHeaderRewriter(
utbot::ProjectContext projectContext,
const std::shared_ptr<CompilationDatabase> &compilationDatabase,
std::shared_ptr<Fetcher::FileToStringSet> structsToDeclare,
fs::path serverBuildDir)
fs::path serverBuildDir,
const types::TypesHandler &typesHandler)
: projectContext(std::move(projectContext)),
clangToolRunner(compilationDatabase), structsToDeclare(structsToDeclare),
serverBuildDir(std::move(serverBuildDir)) {
serverBuildDir(std::move(serverBuildDir)), typesHandler(typesHandler) {
}

std::unique_ptr<clang::tooling::FrontendActionFactory>
SourceToHeaderRewriter::createFactory(llvm::raw_ostream *externalStream,
llvm::raw_ostream *internalStream,
llvm::raw_ostream *unnamedTypeDeclsStream,
llvm::raw_ostream *wrapperStream,
fs::path sourceFilePath,
bool forStubHeader) {
if (Paths::isCXXFile(sourceFilePath)) {
externalStream = nullptr;
internalStream = nullptr;
}
fetcherInstance = std::make_unique<SourceToHeaderMatchCallback>(
projectContext, sourceFilePath, externalStream, internalStream, wrapperStream, forStubHeader);
projectContext, sourceFilePath, externalStream, internalStream, unnamedTypeDeclsStream, wrapperStream, typesHandler, forStubHeader);
finder = std::make_unique<clang::ast_matchers::MatchFinder>();
finder->addMatcher(Matchers::anyToplevelDeclarationMatcher, fetcherInstance.get());
return clang::tooling::newFrontendActionFactory(finder.get());
Expand All @@ -40,8 +46,10 @@ SourceToHeaderRewriter::generateSourceDeclarations(const fs::path &sourceFilePat
llvm::raw_string_ostream externalStream(externalDeclarations);
std::string internalDeclarations;
llvm::raw_string_ostream internalStream(internalDeclarations);
std::string unnamedTypeDeclarations;
llvm::raw_string_ostream unnamedTypeDeclsStream(unnamedTypeDeclarations);

auto factory = createFactory(&externalStream, &internalStream, nullptr, sourceFilePath, forStubHeader);
auto factory = createFactory(&externalStream, &internalStream, &unnamedTypeDeclsStream, nullptr, sourceFilePath, forStubHeader);

if (CollectionUtils::containsKey(*structsToDeclare, sourceFilePath)) {
std::stringstream newContentStream;
Expand All @@ -57,14 +65,17 @@ SourceToHeaderRewriter::generateSourceDeclarations(const fs::path &sourceFilePat
}
externalStream.flush();
internalStream.flush();
unnamedTypeDeclsStream.flush();

return { externalDeclarations, internalDeclarations };
return { externalDeclarations, internalDeclarations, unnamedTypeDeclarations };
}


std::string SourceToHeaderRewriter::generateTestHeader(const fs::path &sourceFilePath,
const Tests &test) {
MEASURE_FUNCTION_EXECUTION_TIME
auto sourceDeclarations = generateSourceDeclarations(sourceFilePath, false);

if (Paths::isCXXFile(sourceFilePath)) {
auto sourceFileToInclude = sourceFilePath;
if (test.mainHeader.has_value()) {
Expand All @@ -73,12 +84,11 @@ std::string SourceToHeaderRewriter::generateTestHeader(const fs::path &sourceFil
}
sourceFileToInclude = fs::relative(sourceFilePath, test.testHeaderFilePath.parent_path());
return StringUtils::stringFormat("#define main main__\n\n"
"#include \"%s\"\n\n",
sourceFileToInclude);
"#include \"%s\"\n\n"
"%s\n",
sourceFileToInclude, sourceDeclarations.unnamedTypeDeclarations);
}

auto sourceDeclarations = generateSourceDeclarations(sourceFilePath, false);

return StringUtils::stringFormat(
"%s\n"
"namespace %s {\n"
Expand All @@ -88,13 +98,14 @@ std::string SourceToHeaderRewriter::generateTestHeader(const fs::path &sourceFil
"%s\n"
"%s\n"
"%s\n"
"}\n"
"%s\n",
"%s\n"
"\n%s"
"}\n",
Copyright::GENERATED_C_CPP_FILE_HEADER, PrinterUtils::TEST_NAMESPACE,
NameDecorator::DEFINES_CODE, PrinterUtils::DEFINES_FOR_C_KEYWORDS,
PrinterUtils::KNOWN_IMPLICIT_RECORD_DECLS_CODE,
sourceDeclarations.externalDeclarations, sourceDeclarations.internalDeclarations,
NameDecorator::UNDEF_WCHAR_T, NameDecorator::UNDEFS_CODE);
NameDecorator::UNDEF_WCHAR_T, NameDecorator::UNDEFS_CODE, sourceDeclarations.unnamedTypeDeclarations);
}

std::string SourceToHeaderRewriter::generateStubHeader(const fs::path &sourceFilePath) {
Expand Down Expand Up @@ -122,7 +133,7 @@ std::string SourceToHeaderRewriter::generateWrapper(const fs::path &sourceFilePa
}
std::string result;
llvm::raw_string_ostream wrapperStream(result);
auto factory = createFactory(nullptr, nullptr, &wrapperStream, sourceFilePath, false);
auto factory = createFactory(nullptr, nullptr, nullptr, &wrapperStream, sourceFilePath, false);
clangToolRunner.run(sourceFilePath, factory.get());
wrapperStream.flush();
return result;
Expand Down
6 changes: 5 additions & 1 deletion server/src/clang-utils/SourceToHeaderRewriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,15 @@ class SourceToHeaderRewriter {
fs::path projectPath;
fs::path serverBuildDir;
std::shared_ptr<Fetcher::FileToStringSet> structsToDeclare;
const types::TypesHandler &typesHandler;

std::unique_ptr<clang::ast_matchers::MatchFinder::MatchCallback> fetcherInstance;
std::unique_ptr<clang::ast_matchers::MatchFinder> finder;

std::unique_ptr<clang::tooling::FrontendActionFactory>
createFactory(llvm::raw_ostream *externalStream,
llvm::raw_ostream *internalStream,
llvm::raw_ostream *unnamedTypeDeclsStream,
llvm::raw_ostream *wrapperStream,
fs::path sourceFilePath,
bool forStubHeader);
Expand All @@ -39,6 +41,7 @@ class SourceToHeaderRewriter {
struct SourceDeclarations {
std::string externalDeclarations;
std::string internalDeclarations;
std::string unnamedTypeDeclarations;
};

friend class SourceToHeaderMatchCallback;
Expand All @@ -47,7 +50,8 @@ class SourceToHeaderRewriter {
utbot::ProjectContext projectContext,
const std::shared_ptr<CompilationDatabase> &compilationDatabase,
std::shared_ptr<Fetcher::FileToStringSet> structsToDeclare,
fs::path serverBuildDir);
fs::path serverBuildDir,
const types::TypesHandler &typesHandler);

SourceDeclarations generateSourceDeclarations(const fs::path &sourceFilePath, bool forStubHeader);

Expand Down
5 changes: 3 additions & 2 deletions server/src/types/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,7 @@ namespace types {

struct Field {
types::Type type;
bool unnamedType;
bool anonymous;
std::string name;
/// size in @b bits
Expand Down Expand Up @@ -366,7 +367,7 @@ namespace types {
size_t maximumAlignment = 16; /// maximumAlignment in @b bytes
};

explicit TypesHandler(TypeMaps &types, SizeContext sizeContext)
explicit TypesHandler(const TypeMaps &types, SizeContext sizeContext)
: typeMaps(types), sizeContext(sizeContext){};

/**
Expand Down Expand Up @@ -650,7 +651,7 @@ namespace types {
};

private:
TypeMaps &typeMaps;
const TypeMaps &typeMaps;
SizeContext sizeContext;
mutable tsl::ordered_set<TypeName> recursiveCheckStarted{};
mutable std::unordered_map<IsSupportedTypeArguments,
Expand Down
10 changes: 9 additions & 1 deletion server/src/types/TypesResolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,16 +63,20 @@ std::string TypesResolver::getFullname(const clang::TagDecl *TD, const clang::Qu
uint64_t id, const fs::path &sourceFilePath) {
auto pp = clang::PrintingPolicy(clang::LangOptions());
pp.SuppressTagKeyword = true;
bool typeDeclNeeded = canonicalType->hasUnnamedOrLocalType() && !fullname[id].empty();
std::string currentStructName = canonicalType.getNonReferenceType().getUnqualifiedType().getAsString(pp);
fullname.insert(std::make_pair(id, currentStructName));

if (Paths::getSourceLanguage(sourceFilePath) == utbot::Language::C) {
if (Paths::getSourceLanguage(sourceFilePath) == utbot::Language::C || typeDeclNeeded) {
if (const auto *parentNode = llvm::dyn_cast<const clang::RecordDecl>(TD->getLexicalParent())) {
clang::QualType parentCanonicalType = parentNode->getASTContext().getTypeDeclType(
parentNode).getCanonicalType();
uint64_t parentID = types::Type::getIdFromCanonicalType(parentCanonicalType);
if (!fullname[parentID].empty()) {
fullname[id] = fullname[parentID] + "::" + fullname[id];
if (typeDeclNeeded) {
StringUtils::replaceAll(fullname[id], "::", "_");
}
}
}
}
Expand Down Expand Up @@ -132,6 +136,10 @@ void TypesResolver::resolveStructEx(const clang::RecordDecl *D, const std::strin

const clang::QualType paramType = F->getType().getCanonicalType();
field.type = types::Type(paramType, paramType.getAsString(), sourceManager);
field.unnamedType = field.type.isUnnamed();
if (field.unnamedType && !field.anonymous) {
fullname[field.type.getId()] = field.name;
}
if (field.type.isPointerToFunction()) {
structInfo.functionFields[field.name] = ParamsHandler::getFunctionPointerDeclaration(
F->getFunctionType(), field.name, sourceManager,
Expand Down
Loading

0 comments on commit 593344f

Please sign in to comment.