github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/tools/clang/codesearch/codesearch.cpp (about) 1 // Copyright 2025 syzkaller project authors. All rights reserved. 2 // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 4 #include "json.h" 5 #include "output.h" 6 7 #include "clang/AST/ASTContext.h" 8 #include "clang/AST/Comment.h" 9 #include "clang/AST/Decl.h" 10 #include "clang/AST/DeclarationName.h" 11 #include "clang/AST/RecursiveASTVisitor.h" 12 #include "clang/Basic/SourceManager.h" 13 #include "clang/Frontend/CompilerInstance.h" 14 #include "clang/Tooling/CommonOptionsParser.h" 15 #include "clang/Tooling/Tooling.h" 16 #include "llvm/Support/Casting.h" 17 #include "llvm/Support/CommandLine.h" 18 #include "llvm/Support/ErrorHandling.h" 19 20 #include <algorithm> 21 #include <filesystem> 22 #include <string> 23 #include <unordered_map> 24 25 using namespace clang; 26 27 // MacroDef/MacroMap hold information about macros defined in the file. 28 struct MacroDef { 29 std::string Value; // value as written in the source 30 SourceRange SourceRange; // soruce range of the value 31 }; 32 using MacroMap = std::unordered_map<std::string, MacroDef>; 33 34 class Instance : public tooling::SourceFileCallbacks { 35 public: 36 Instance(Output& Output) : Output(Output) {} 37 std::unique_ptr<ASTConsumer> newASTConsumer(); 38 39 private: 40 Output& Output; 41 MacroMap Macros; 42 43 bool handleBeginSource(CompilerInstance& CI) override; 44 }; 45 46 // PPCallbacksTracker records all macro definitions (name/value/source location). 47 class PPCallbacksTracker : public PPCallbacks { 48 public: 49 PPCallbacksTracker(Preprocessor& PP, MacroMap& Macros) : SM(PP.getSourceManager()), Macros(Macros) {} 50 51 private: 52 SourceManager& SM; 53 MacroMap& Macros; 54 55 void MacroDefined(const Token& MacroName, const MacroDirective* MD) override { (void)Macros; } 56 }; 57 58 class IndexerAstConsumer : public ASTConsumer { 59 public: 60 IndexerAstConsumer(Output& Output, const MacroMap& Macros) : Output(Output), Macros(Macros) {} 61 62 private: 63 Output& Output; 64 const MacroMap& Macros; 65 66 void HandleTranslationUnit(ASTContext& context) override; 67 }; 68 69 class Indexer : public RecursiveASTVisitor<Indexer> { 70 public: 71 Indexer(ASTContext& Context, Output& Output, const MacroMap& Macros) 72 : Context(Context), SM(Context.getSourceManager()), Output(Output) {} 73 74 bool VisitFunctionDecl(const FunctionDecl*); 75 76 private: 77 ASTContext& Context; 78 SourceManager& SM; 79 Output& Output; 80 }; 81 82 bool Instance::handleBeginSource(CompilerInstance& CI) { 83 Preprocessor& PP = CI.getPreprocessor(); 84 PP.addPPCallbacks(std::make_unique<PPCallbacksTracker>(PP, Macros)); 85 return true; 86 } 87 88 std::unique_ptr<ASTConsumer> Instance::newASTConsumer() { return std::make_unique<IndexerAstConsumer>(Output, Macros); } 89 90 void IndexerAstConsumer::HandleTranslationUnit(ASTContext& Context) { 91 Indexer Indexer(Context, Output, Macros); 92 Indexer.TraverseDecl(Context.getTranslationUnitDecl()); 93 } 94 95 bool Indexer::VisitFunctionDecl(const FunctionDecl* Func) { 96 if (!Func->doesThisDeclarationHaveABody()) 97 return true; 98 auto Range = Func->getSourceRange(); 99 const std::string& SourceFile = std::filesystem::relative(SM.getFilename(SM.getExpansionLoc(Range.getBegin())).str()); 100 int StartLine = SM.getExpansionLineNumber(Range.getBegin()); 101 int EndLine = SM.getExpansionLineNumber(Range.getEnd()); 102 std::string CommentSourceFile; 103 int CommentStartLine = 0; 104 int CommentEndLine = 0; 105 if (auto Comment = Context.getRawCommentForDeclNoCache(Func)) { 106 const auto& begin = Comment->getBeginLoc(); 107 const auto& end = Comment->getEndLoc(); 108 CommentSourceFile = std::filesystem::relative(SM.getFilename(SM.getExpansionLoc(begin)).str()); 109 CommentStartLine = SM.getExpansionLineNumber(begin); 110 CommentEndLine = SM.getExpansionLineNumber(end); 111 // Expand body range to include the comment, if they intersect. 112 if (SourceFile == CommentSourceFile && 113 std::max(StartLine, CommentStartLine) <= std::min(EndLine, CommentEndLine) + 1) { 114 StartLine = std::min(StartLine, CommentStartLine); 115 EndLine = std::max(EndLine, CommentEndLine); 116 } 117 } 118 Output.emit(Definition{ 119 .Kind = KindFunction, 120 .Name = Func->getNameAsString(), 121 .Type = Func->getType().getAsString(), 122 .IsStatic = Func->isStatic(), 123 .Body = 124 LineRange{ 125 .File = SourceFile, 126 .StartLine = StartLine, 127 .EndLine = EndLine, 128 }, 129 .Comment = 130 LineRange{ 131 .File = CommentSourceFile, 132 .StartLine = CommentStartLine, 133 .EndLine = CommentEndLine, 134 }, 135 }); 136 return true; 137 } 138 139 int main(int argc, const char** argv) { 140 llvm::cl::OptionCategory Options("syz-indexer options"); 141 auto OptionsParser = tooling::CommonOptionsParser::create(argc, argv, Options); 142 if (!OptionsParser) { 143 llvm::errs() << OptionsParser.takeError(); 144 return 1; 145 } 146 Output Output; 147 Instance Instance(Output); 148 tooling::ClangTool Tool(OptionsParser->getCompilations(), OptionsParser->getSourcePathList()); 149 if (Tool.run(tooling::newFrontendActionFactory(&Instance, &Instance).get())) 150 return 1; 151 Output.print(); 152 return 0; 153 }