9 #include "clang/AST/ASTContext.h"
10 #include "clang/Frontend/CompilerInstance.h"
11 #include "clang/Frontend/FrontendActions.h"
12 #include "clang/Rewrite/Core/Rewriter.h"
13 #include "clang/Tooling/CommonOptionsParser.h"
14 #include "clang/Tooling/Tooling.h"
15 #include "llvm/Support/MemoryBuffer.h"
16 #include "llvm/Support/Path.h"
17 #include "llvm/Support/Signals.h"
18 #include "llvm/Support/raw_ostream.h"
28 using namespace clang;
29 using namespace clang::driver;
30 using namespace clang::tooling;
54 "Insights-Educational"sv,
55 "This transformations are only for education purposes. The resulting code most likely does not compile."sv);
59 llvm::cl::desc(
"Read the input from <stdin>."sv),
60 llvm::cl::init(
false),
64 static llvm::cl::opt<bool>
68 #define INSIGHTS_OPT(option, name, deflt, description, category) \
69 static llvm::cl::opt<bool, true> g##name(option, \
70 llvm::cl::desc(std::string_view{description}), \
71 llvm::cl::NotHidden, \
72 llvm::cl::location(gInsightsOptions.name), \
73 llvm::cl::init(deflt), \
74 llvm::cl::cat(category));
77 #include "InsightsOptions.def"
80 static const ASTContext*
gAST{};
87 static const CompilerInstance*
gCI{};
99 static constinit std::array<GlobalInsertMap, static_cast<size_t>(GlobalInserts::MAX)>
gGlobalInserts{};
119 std::vector<IncludeData>& mIncludes;
122 FindIncludes(SourceManager& sm, Preprocessor& pp, std::vector<IncludeData>& incData)
135 OptionalFileEntryRef ,
140 SrcMgr::CharacteristicKind )
override
142 auto expansionLoc = mSm.getExpansionLoc(hashLoc);
144 if(expansionLoc.isInvalid() or mSm.isInSystemHeader(expansionLoc)) {
150 mIncludes.emplace_back(expansionLoc,
StrCat(
"#include <"sv, fileName,
">\n"sv));
153 mIncludes.emplace_back(expansionLoc,
StrCat(
"#include \""sv, fileName,
"\"\n"sv));
157 void MacroDefined(
const Token& macroNameTok,
const MacroDirective* md)
override
159 const auto loc = md->getLocation();
160 if(not mSm.isWrittenInMainFile(loc)) {
164 auto name = mPP.getSpelling(macroNameTok);
166 if(not name.starts_with(
"INSIGHTS_"sv)) {
170 mIncludes.emplace_back(loc,
StrCat(
"#define "sv, name,
"\n"sv));
177 std::vector<IncludeData>& mIncludes;
182 , mRewriter{rewriter}
183 , mIncludes{includes}
201 auto& sm = context.getSourceManager();
203 auto isExpansionInSystemHeader = [&sm](
const Decl* d) {
204 auto expansionLoc = sm.getExpansionLoc(d->getLocation());
206 return expansionLoc.isInvalid() or sm.isInSystemHeader(expansionLoc);
209 const auto& mainFileId = sm.getMainFileID();
211 mRewriter.ReplaceText({sm.getLocForStartOfFile(mainFileId), sm.getLocForEndOfFile(mainFileId)},
"");
216 auto include = mIncludes.begin();
218 auto insertBlankLineIfRequired = [&](std::optional<SourceLocation>& lastLoc, SourceLocation nextLoc) {
219 if(lastLoc.has_value() and
220 (2 <= (sm.getSpellingLineNumber(nextLoc) - sm.getSpellingLineNumber(lastLoc.value())))) {
221 outputFormatHelper.AppendNewLine();
227 for(std::optional<SourceLocation> lastLoc{};
const auto* d : context.getTranslationUnitDecl()->decls()) {
228 if(isExpansionInSystemHeader(d)) {
233 for(; (mIncludes.end() != include) and (include->first < d->getLocation()); include = std::next(include)) {
234 insertBlankLineIfRequired(lastLoc, include->first);
235 outputFormatHelper.Append(include->second);
239 include = std::find_if_not(include, mIncludes.end(), [&](
auto& inc) {
240 return ((inc.first >= d->getLocation()) and (inc.first <= d->getEndLoc()));
243 if(isa<LinkageSpecDecl>(d) and d->isImplicit()) {
248 }
else if(
const auto* vdspec = dyn_cast_or_null<VarTemplateSpecializationDecl>(d);
249 vdspec and (TSK_ExplicitSpecialization != vdspec->getSpecializationKind())) {
253 insertBlankLineIfRequired(lastLoc, d->getLocation());
255 codeGenerator->InsertArg(d);
258 std::string insightsIncludes{};
261 insightsIncludes.append(
262 R
"(/*************************************************************************************
263 * NOTE: The coroutine transformation you've enabled is a hand coded transformation! *
264 * Most of it is _not_ present in the AST. What you see is an approximation. *
265 *************************************************************************************/
268 insightsIncludes.append(
269 R
"(/*************************************************************************************
270 * NOTE: This an educational hand-rolled transformation. Things can be incorrect or *
272 *************************************************************************************/
278 std::string inserts{};
284 inserts.append(value);
285 inserts.append(
"\n"sv);
288 if(not inserts.empty()) {
289 insightsIncludes.append(inserts);
290 insightsIncludes.append(
"\n");
293 outputFormatHelper.InsertAt(0, insightsIncludes);
295 mRewriter.InsertText(sm.getLocForStartOfFile(mainFileId), outputFormatHelper.GetString());
298 const auto& fileEntry = sm.getFileEntryForID(mainFileId);
300 const auto cxaLoc = sm.translateFileLineCol(fileEntry, fileEntry->getSize(), 1);
302 mRewriter.InsertText(cxaLoc,
cxaStart);
310 Rewriter mRewriter{};
311 std::vector<IncludeData> mIncludes{};
317 mRewriter.getEditBuffer(mRewriter.getSourceMgr().getMainFileID()).write(llvm::outs());
324 Preprocessor& pp = CI.getPreprocessor();
325 pp.addPPCallbacks(std::make_unique<FindIncludes>(CI.getSourceManager(), pp, mIncludes));
327 mRewriter.setSourceMgr(CI.getSourceManager(), CI.getLangOpts());
328 return std::make_unique<CppInsightASTConsumer>(mRewriter, mIncludes);
333 #include "clang/Basic/Version.h"
337 ostream <<
"cpp-insights " << INSIGHTS_VERSION <<
" https://cppinsights.io (" << GIT_REPO_URL <<
" "
338 << GIT_COMMIT_HASH <<
")"
341 #ifdef INSIGHTS_DEBUG
342 ostream <<
" Build with debug enabled\n";
344 ostream <<
" LLVM Revision: " << clang::getLLVMRevision() <<
'\n';
345 ostream <<
" Clang Revision: " << clang::getClangFullCPPVersion() <<
'\n';
349 int main(
int argc,
const char** argv)
354 "#include <new> // for thread-safe static's placement new\n#include <stdint.h> // for "
355 "uint64_t under Linux/GCC"sv);
371 R"(extern "C" void* __cxa_vec_new(void*, unsigned int, unsigned int, unsigned int, void* (*)(void*), void* (*)(void*));)"sv);
374 R"(extern "C" void* __cxa_vec_ctor(void*, unsigned int, unsigned int, unsigned int, void* (*)(void*), void* (*)(void*));)"sv);
377 R"(extern "C" void __cxa_vec_delete(void *, unsigned int, unsigned int, void* (*destructor)(void *) );)"sv);
380 R"(extern "C" void __cxa_vec_dtor(void *, unsigned int, unsigned int, void* (*destructor)(void *) );)"sv);
390 extern struct __mptr* __vtbl_array[];
394 llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
397 auto opExpected = CommonOptionsParser::create(argc, argv,
gInsightCategory);
399 if(
auto err = opExpected.takeError()) {
400 llvm::errs() << toString(std::move(err)) <<
"\n";
407 std::unique_ptr<llvm::MemoryBuffer> inMemoryCode{};
409 CommonOptionsParser& op{opExpected.get()};
410 ClangTool tool(op.getCompilations(), op.getSourcePathList());
413 if(op.getSourcePathList().size() != 1) {
414 llvm::errs() <<
"Expect exactly one file path in STDINMode.\n"sv;
418 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> codeOrErr = llvm::MemoryBuffer::getSTDIN();
420 if(
const std::error_code errorCode = codeOrErr.getError()) {
421 llvm::errs() << errorCode.message() <<
"\n";
425 inMemoryCode = std::move(codeOrErr.get());
427 if(inMemoryCode->getBufferSize() == 0) {
428 Error(
"empty file\n");
432 llvm::StringRef sourceFilePath = op.getSourcePathList().front();
433 tool.mapVirtualFile(sourceFilePath, inMemoryCode->getBuffer());
436 auto prependArgument = [&](
auto arg) {
437 tool.appendArgumentsAdjuster(getInsertArgumentAdjuster(arg, ArgumentInsertPosition::BEGIN));
448 prependArgument(INSIGHTS_LLVM_INCLUDE_DIR);
449 prependArgument(
"-stdlib=libc++");
450 prependArgument(
"-fexperimental-library");
453 prependArgument(
"-nostdinc++");
457 prependArgument(INSIGHTS_CLANG_RESOURCE_INCLUDE_DIR);
458 prependArgument(INSIGHTS_CLANG_RESOURCE_DIR);
465 return tool.run(newFrontendActionFactory<CppInsightFrontendAction>().get());
std::pair< const SourceLocation, std::string > IncludeData
int main(int argc, const char **argv)
static llvm::cl::OptionCategory gInsightCategory("Insights"sv)
InsightsOptions & GetInsightsOptionsRW()
static llvm::cl::opt< bool > gStdinMode("stdin", llvm::cl::desc("Read the input from <stdin>."sv), llvm::cl::init(false), llvm::cl::cat(gInsightCategory))
const CompilerInstance & GetGlobalCI()
Get access to the CompilerInstance.
static const ASTContext * gAST
static InsightsOptions gInsightsOptions
static void PrintVersion(raw_ostream &ostream)
const ASTContext & GetGlobalAST()
Get access to the ASTContext.
static llvm::cl::OptionCategory gInsightEduCategory("Insights-Educational"sv, "This transformations are only for education purposes. The resulting code most likely does not compile."sv)
const InsightsOptions & GetInsightsOptions()
Get the global C++ Insights options.
static const CompilerInstance * gCI
static llvm::cl::opt< bool > gUseLibCpp("use-libc++", llvm::cl::desc("Use libc++."sv), llvm::cl::init(false), llvm::cl::cat(gInsightCategory))
constexpr std::string_view cxaStart
void HandleTranslationUnit(ASTContext &context) override
CppInsightASTConsumer(Rewriter &rewriter, std::vector< IncludeData > &includes)
CppInsightFrontendAction()=default
std::unique_ptr< ASTConsumer > CreateASTConsumer(CompilerInstance &CI, StringRef) override
void EndSourceFileAction() override
void MacroDefined(const Token ¯oNameTok, const MacroDirective *md) override
FindIncludes(SourceManager &sm, Preprocessor &pp, std::vector< IncludeData > &incData)
void InclusionDirective(SourceLocation hashLoc, const Token &, StringRef fileName, bool isAngled, CharSourceRange, OptionalFileEntryRef, StringRef, StringRef, const Module *, bool, SrcMgr::CharacteristicKind) override
A special container which creates either a CodeGenerator or a CfrontCodeGenerator depending on the co...
void StrCat(std::string &ret, const auto &... args)
static constinit std::array< GlobalInsertMap, static_cast< size_t >GlobalInserts::MAX)> gGlobalInserts
@ HeaderStdlib
Track whether we need to insert <stdlib.h> in Cfront mode.
@ HeaderUtility
Track whether there was a std::move inserted.
@ HeaderAssert
Track whether we need to insert <assert.h> in Cfront mode.
@ HeaderException
Track whether there was a noexcept transformation requireing the exception header.
@ HeaderStddef
Track whether we need to insert <stddef.h> in Cfront mode.
void Error(const char *fmt, const auto &... args)
Log an error.
std::string EmitGlobalVariableCtors()
std::pair< bool, std::string_view > GlobalInsertMap
void AddGLobalInsertMapEntry(GlobalInserts idx, std::string_view value)
void EnableGlobalInsert(GlobalInserts idx)
Global C++ Insights command line options.