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  }