From feb61342aaf3e5ef2bcc36b3d512eba600e03c9c Mon Sep 17 00:00:00 2001
From: cokeom <cokelin@hnu.edu.cn>
Date: Fri, 14 Oct 2022 18:30:00 +0800
Subject: [PATCH] Implement -aux-info option.

---
 include/clang/AST/DeclBase.h                  |  2 +
 include/clang/AST/PrettyPrinter.h             |  6 +-
 include/clang/Driver/Options.td               |  7 ++
 include/clang/Frontend/ASTConsumers.h         |  3 +
 include/clang/Frontend/FrontendActions.h      |  6 ++
 include/clang/Frontend/FrontendOptions.h      |  3 +
 lib/AST/DeclPrinter.cpp                       | 96 +++++++++++++++++++
 lib/AST/TypePrinter.cpp                       | 23 +++--
 lib/Driver/Driver.cpp                         |  6 +-
 lib/Driver/ToolChains/Clang.cpp               |  7 ++
 lib/Frontend/ASTConsumers.cpp                 | 34 +++++++
 lib/Frontend/CompilerInvocation.cpp           |  7 ++
 lib/Frontend/FrontendActions.cpp              | 13 +++
 .../ExecuteCompilerInvocation.cpp             |  2 +
 test/Frontend/print-function-prototype.c      | 28 ++++++
 15 files changed, 230 insertions(+), 13 deletions(-)
 create mode 100644 test/Frontend/print-function-prototype.c

diff --git a/include/clang/AST/DeclBase.h b/include/clang/AST/DeclBase.h
index 15eb29f7..a46b13e4 100644
--- a/include/clang/AST/DeclBase.h
+++ b/include/clang/AST/DeclBase.h
@@ -1164,6 +1164,8 @@ public:
              bool PrintInstantiation = false) const;
   void print(raw_ostream &Out, const PrintingPolicy &Policy,
              unsigned Indentation = 0, bool PrintInstantiation = false) const;
+  void printDeclPrototype(raw_ostream &Out, unsigned Indentation = 0,
+                bool PrintInstantiation = false) const;
   static void printGroup(Decl** Begin, unsigned NumDecls,
                          raw_ostream &Out, const PrintingPolicy &Policy,
                          unsigned Indentation = 0);
diff --git a/include/clang/AST/PrettyPrinter.h b/include/clang/AST/PrettyPrinter.h
index 3baf2b2b..f147fd87 100644
--- a/include/clang/AST/PrettyPrinter.h
+++ b/include/clang/AST/PrettyPrinter.h
@@ -74,7 +74,8 @@ struct PrintingPolicy {
         MSWChar(LO.MicrosoftExt && !LO.WChar), IncludeNewlines(true),
         MSVCFormatting(false), ConstantsAsWritten(false),
         SuppressImplicitBase(false), FullyQualifiedName(false),
-        PrintCanonicalTypes(false), PrintInjectedClassNameWithArguments(true) {}
+        PrintCanonicalTypes(false), PrintInjectedClassNameWithArguments(true),
+        PrintRestrict(true) {}
 
   /// Adjust this printing policy for cases where it's known that we're
   /// printing C++ code (for instance, if AST dumping reaches a C++-only
@@ -273,6 +274,9 @@ struct PrintingPolicy {
   /// invalid C++ code.
   unsigned PrintInjectedClassNameWithArguments : 1;
 
+  /// Whether to print the keyword "restrict" in the function prototype.
+  unsigned PrintRestrict : 1;
+
   /// Callbacks to use to allow the behavior of printing to be customized.
   const PrintingCallbacks *Callbacks = nullptr;
 };
diff --git a/include/clang/Driver/Options.td b/include/clang/Driver/Options.td
index 04521197..2126f0df 100644
--- a/include/clang/Driver/Options.td
+++ b/include/clang/Driver/Options.td
@@ -491,6 +491,13 @@ def convert : Separate<["-"], "convert">,
               Group<clang_replaced_options_Group>,
               HelpText<"Generate big-endian/little-endian code">;
 
+def aux_info : Separate<["-"], "aux-info">,
+               Flags<[NoXarchOption, CoreOption, CC1Option]>,
+               Group<Action_Group>,
+               HelpText<"Output to the given filename prototyped declarations "
+                        "for all functions declared and/or defined in a "
+                        "translation unit, including those in header files">;
+
 // Developer Driver Options
 
 def internal_Group : OptionGroup<"<clang internal options>">, Flags<[HelpHidden]>;
diff --git a/include/clang/Frontend/ASTConsumers.h b/include/clang/Frontend/ASTConsumers.h
index 98cfc7ca..ecd3c268 100644
--- a/include/clang/Frontend/ASTConsumers.h
+++ b/include/clang/Frontend/ASTConsumers.h
@@ -50,6 +50,9 @@ std::unique_ptr<ASTConsumer> CreateASTDeclNodeLister();
 // function declarations to stderr.
 std::unique_ptr<ASTConsumer> CreateASTViewer();
 
+std::unique_ptr<ASTConsumer>
+CreateASTFunctionPrinter(std::unique_ptr<llvm::raw_fd_ostream> Out);
+
 } // end clang namespace
 
 #endif
diff --git a/include/clang/Frontend/FrontendActions.h b/include/clang/Frontend/FrontendActions.h
index 25ca9598..d2b294a5 100644
--- a/include/clang/Frontend/FrontendActions.h
+++ b/include/clang/Frontend/FrontendActions.h
@@ -74,6 +74,12 @@ protected:
                                                  StringRef InFile) override;
 };
 
+class ASTFunctionPrinterAction : public ASTFrontendAction {
+protected:
+  std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
+                                                 StringRef InFile) override;
+};
+
 class GeneratePCHAction : public ASTFrontendAction {
 protected:
   std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
diff --git a/include/clang/Frontend/FrontendOptions.h b/include/clang/Frontend/FrontendOptions.h
index 223c1e05..3362a2fc 100644
--- a/include/clang/Frontend/FrontendOptions.h
+++ b/include/clang/Frontend/FrontendOptions.h
@@ -44,6 +44,9 @@ enum ActionKind {
   /// Parse ASTs and view them in Graphviz.
   ASTView,
 
+  /// Print function prototype.
+  ASTFunctionPrinter,
+
   /// Dump the compiler configuration.
   DumpCompilerOptions,
 
diff --git a/lib/AST/DeclPrinter.cpp b/lib/AST/DeclPrinter.cpp
index ca64f8f6..a857466e 100644
--- a/lib/AST/DeclPrinter.cpp
+++ b/lib/AST/DeclPrinter.cpp
@@ -115,6 +115,7 @@ namespace {
     void prettyPrintAttributes(Decl *D);
     void prettyPrintPragmas(Decl *D);
     void printDeclType(QualType T, StringRef DeclName, bool Pack = false);
+    void printFunctionDeclPrototype(FunctionDecl *D);
   };
 }
 
@@ -135,6 +136,14 @@ void TemplateParameterList::print(raw_ostream &Out, const ASTContext &Context,
   print(Out, Context, Context.getPrintingPolicy(), OmitTemplateKW);
 }
 
+void Decl::printDeclPrototype(raw_ostream &Out, unsigned Indentation,
+                              bool PrintInstantiation) const {
+  DeclPrinter Printer(Out, getASTContext().getPrintingPolicy(), getASTContext(),
+                      Indentation, PrintInstantiation);
+  auto *FD = const_cast<FunctionDecl *>(dyn_cast<FunctionDecl>(this));
+  Printer.printFunctionDeclPrototype(FD);
+}
+
 void TemplateParameterList::print(raw_ostream &Out, const ASTContext &Context,
                                   const PrintingPolicy &Policy,
                                   bool OmitTemplateKW) const {
@@ -784,6 +793,93 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) {
   }
 }
 
+void DeclPrinter::printFunctionDeclPrototype(FunctionDecl *D) {
+  if (!Policy.SuppressSpecifiers) {
+    switch (D->getStorageClass()) {
+    case SC_None:
+    case SC_Extern:
+    case SC_PrivateExtern:
+      Out << "extern ";
+      break;
+    case SC_Static:
+      Out << "static ";
+      break;
+    case SC_Auto:
+    case SC_Register:
+      llvm_unreachable("invalid for functions");
+    }
+  }
+
+  PrintingPolicy SubPolicy(Policy);
+  SubPolicy.SuppressSpecifiers = false;
+  SubPolicy.PrintRestrict = false;
+  std::string Proto;
+
+  /// Print the function name.
+  if (Policy.FullyQualifiedName) {
+    Proto += D->getQualifiedNameAsString();
+  } else {
+    llvm::raw_string_ostream OS(Proto);
+    if (!Policy.SuppressScope) {
+      if (const NestedNameSpecifier *NS = D->getQualifier()) {
+        NS->print(OS, Policy);
+      }
+    }
+    D->getNameInfo().printName(OS, Policy);
+  }
+
+  QualType Ty = D->getType();
+  while (const ParenType *PT = dyn_cast<ParenType>(Ty)) {
+    Proto = '(' + Proto + ')';
+    Ty = PT->getInnerType();
+  }
+  if (const FunctionType *AFT = Ty->getAs<FunctionType>()) {
+    const FunctionProtoType *FT = nullptr;
+    if (D->hasWrittenPrototype())
+      FT = dyn_cast<FunctionProtoType>(AFT);
+
+    Proto += "(";
+
+    /// Print function parameters.
+    if (FT) {
+      llvm::raw_string_ostream POut(Proto);
+      for (unsigned i = 0, e = D->getNumParams(); i != e; ++i) {
+        if (i)
+          POut << ", ";
+        ParmVarDecl *PVD = D->getParamDecl(i);
+        QualType T = PVD->getTypeSourceInfo()
+                         ? PVD->getTypeSourceInfo()->getType()
+                         : PVD->getASTContext().getUnqualifiedObjCPointerType(
+                               PVD->getType());
+        T.print(POut, SubPolicy, "", Indentation);
+      }
+
+      if (FT->isVariadic()) {
+        if (D->getNumParams())
+          POut << ", ";
+        POut << "...";
+      } else if (!D->getNumParams()) {
+        POut << "void";
+      }
+    } else if (D->doesThisDeclarationHaveABody() && !D->hasPrototype()) {
+      for (unsigned i = 0, e = D->getNumParams(); i != e; ++i) {
+        if (i)
+          Proto += ", ";
+        Proto += D->getParamDecl(i)->getNameAsString();
+      }
+      if (!D->getNumParams()) {
+        Proto += "void";
+      }
+    }
+    Proto += ")";
+
+    /// Print the function return type.
+    AFT->getReturnType().print(Out, Policy, Proto);
+  } else {
+    Ty.print(Out, Policy, Proto);
+  }
+}
+
 void DeclPrinter::VisitFriendDecl(FriendDecl *D) {
   if (TypeSourceInfo *TSI = D->getFriendType()) {
     unsigned NumTPLists = D->getFriendTypeNumTemplateParameterLists();
diff --git a/lib/AST/TypePrinter.cpp b/lib/AST/TypePrinter.cpp
index 25d7874b..37d116ca 100644
--- a/lib/AST/TypePrinter.cpp
+++ b/lib/AST/TypePrinter.cpp
@@ -139,7 +139,7 @@ namespace {
 } // namespace
 
 static void AppendTypeQualList(raw_ostream &OS, unsigned TypeQuals,
-                               bool HasRestrictKeyword) {
+                               bool HasRestrictKeyword, bool PrintRestrict) {
   bool appendSpace = false;
   if (TypeQuals & Qualifiers::Const) {
     OS << "const";
@@ -151,11 +151,13 @@ static void AppendTypeQualList(raw_ostream &OS, unsigned TypeQuals,
     appendSpace = true;
   }
   if (TypeQuals & Qualifiers::Restrict) {
-    if (appendSpace) OS << ' ';
-    if (HasRestrictKeyword) {
-      OS << "restrict";
-    } else {
-      OS << "__restrict";
+    if (PrintRestrict) {
+      if (appendSpace) OS << ' ';
+      if (HasRestrictKeyword) {
+        OS << "restrict";
+      } else {
+        OS << "__restrict";
+      }
     }
   }
 }
@@ -506,8 +508,8 @@ void TypePrinter::printConstantArrayAfter(const ConstantArrayType *T,
                                           raw_ostream &OS) {
   OS << '[';
   if (T->getIndexTypeQualifiers().hasQualifiers()) {
-    AppendTypeQualList(OS, T->getIndexTypeCVRQualifiers(),
-                       Policy.Restrict);
+    AppendTypeQualList(OS, T->getIndexTypeCVRQualifiers(), Policy.Restrict,
+                       Policy.PrintRestrict);
     OS << ' ';
   }
 
@@ -542,7 +544,8 @@ void TypePrinter::printVariableArrayAfter(const VariableArrayType *T,
                                           raw_ostream &OS) {
   OS << '[';
   if (T->getIndexTypeQualifiers().hasQualifiers()) {
-    AppendTypeQualList(OS, T->getIndexTypeCVRQualifiers(), Policy.Restrict);
+    AppendTypeQualList(OS, T->getIndexTypeCVRQualifiers(), Policy.Restrict,
+                       Policy.PrintRestrict);
     OS << ' ';
   }
 
@@ -2149,7 +2152,7 @@ void Qualifiers::print(raw_ostream &OS, const PrintingPolicy& Policy,
 
   unsigned quals = getCVRQualifiers();
   if (quals) {
-    AppendTypeQualList(OS, quals, Policy.Restrict);
+    AppendTypeQualList(OS, quals, Policy.Restrict, Policy.PrintRestrict);
     addSpace = true;
   }
   if (hasUnaligned()) {
diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp
index 95c0e256..ce677965 100644
--- a/lib/Driver/Driver.cpp
+++ b/lib/Driver/Driver.cpp
@@ -309,7 +309,8 @@ phases::ID Driver::getFinalPhase(const DerivedArgList &DAL,
              (PhaseArg = DAL.getLastArg(options::OPT_rewrite_legacy_objc)) ||
              (PhaseArg = DAL.getLastArg(options::OPT__migrate)) ||
              (PhaseArg = DAL.getLastArg(options::OPT__analyze)) ||
-             (PhaseArg = DAL.getLastArg(options::OPT_emit_ast))) {
+             (PhaseArg = DAL.getLastArg(options::OPT_emit_ast)) ||
+             (PhaseArg = DAL.getLastArg(options::OPT_aux_info))) {
     FinalPhase = phases::Compile;
 
   // -S only runs up to the backend.
@@ -3851,7 +3852,8 @@ Action *Driver::ConstructPhaseAction(
     return C.MakeAction<PrecompileJobAction>(Input, OutputTy);
   }
   case phases::Compile: {
-    if (Args.hasArg(options::OPT_fsyntax_only))
+    if (Args.hasArg(options::OPT_fsyntax_only) ||
+        Args.hasArg(options::OPT_aux_info))
       return C.MakeAction<CompileJobAction>(Input, types::TY_Nothing);
     if (Args.hasArg(options::OPT_rewrite_objc))
       return C.MakeAction<CompileJobAction>(Input, types::TY_RewrittenObjC);
diff --git a/lib/Driver/ToolChains/Clang.cpp b/lib/Driver/ToolChains/Clang.cpp
index 706d0431..e7946529 100644
--- a/lib/Driver/ToolChains/Clang.cpp
+++ b/lib/Driver/ToolChains/Clang.cpp
@@ -6258,6 +6258,13 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
     }
   }
 
+  if (Arg *A = Args.getLastArg(options::OPT_aux_info)) {
+    if (Input.getType() == types::TY_C) {
+      CmdArgs.push_back("-aux-info");
+      CmdArgs.push_back(A->getValue());
+    }
+  }
+
   // With -save-temps, we want to save the unoptimized bitcode output from the
   // CompileJobAction, use -disable-llvm-passes to get pristine IR generated
   // by the frontend.
diff --git a/lib/Frontend/ASTConsumers.cpp b/lib/Frontend/ASTConsumers.cpp
index a73cc887..48f999ab 100644
--- a/lib/Frontend/ASTConsumers.cpp
+++ b/lib/Frontend/ASTConsumers.cpp
@@ -212,3 +212,37 @@ void ASTViewer::HandleTopLevelSingleDecl(Decl *D) {
 std::unique_ptr<ASTConsumer> clang::CreateASTViewer() {
   return std::make_unique<ASTViewer>();
 }
+
+//===----------------------------------------------------------------------===//
+/// ASTFunctionPrototype - AST FunctionPrototype print
+namespace {
+  class ASTFunctionPrinter : public ASTConsumer {
+  private:
+    llvm::raw_fd_ostream &Out;
+    std::unique_ptr<llvm::raw_fd_ostream> OwnedOut;
+  public:
+    ASTFunctionPrinter(std::unique_ptr<llvm::raw_fd_ostream> Out)
+        : Out(Out ? *Out : llvm::outs()), OwnedOut(std::move(Out)) {}
+
+    bool HandleTopLevelDecl(DeclGroupRef D) override {
+      for (DeclGroupRef::iterator I = D.begin(), E = D.end(); I != E; ++I)
+        HandleTopLevelSingleDecl(*I);
+      return true;
+    }
+
+    void HandleTopLevelSingleDecl(Decl *D);
+  };
+} // namespace
+
+void ASTFunctionPrinter::HandleTopLevelSingleDecl(Decl *D) {
+  if (isa<FunctionDecl>(D)) {
+    D->printDeclPrototype(Out);
+    Out << ";";
+    Out << '\n';
+  }
+}
+
+std::unique_ptr<ASTConsumer>
+clang::CreateASTFunctionPrinter(std::unique_ptr<llvm::raw_fd_ostream> Out) {
+  return std::make_unique<ASTFunctionPrinter>(std::move(Out));
+}
\ No newline at end of file
diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp
index 5c5cf461..9665b4ed 100644
--- a/lib/Frontend/CompilerInvocation.cpp
+++ b/lib/Frontend/CompilerInvocation.cpp
@@ -1574,6 +1574,8 @@ static InputKind ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args,
       Opts.ProgramAction = frontend::ASTPrint; break;
     case OPT_ast_view:
       Opts.ProgramAction = frontend::ASTView; break;
+    case OPT_aux_info:
+      Opts.ProgramAction = frontend::ASTFunctionPrinter; break;
     case OPT_compiler_options_dump:
       Opts.ProgramAction = frontend::DumpCompilerOptions; break;
     case OPT_dump_raw_tokens:
@@ -2711,6 +2713,7 @@ static bool isStrictlyPreprocessorAction(frontend::ActionKind Action) {
   case frontend::ASTDump:
   case frontend::ASTPrint:
   case frontend::ASTView:
+  case frontend::ASTFunctionPrinter:
   case frontend::EmitAssembly:
   case frontend::EmitBC:
   case frontend::EmitHTML:
@@ -2971,6 +2974,10 @@ bool CompilerInvocation::CreateFromArgs(CompilerInvocation &Res,
 
   FixupInvocation(Res, Diags, Args, DashX);
 
+  if (Arg *A = Args.getLastArg(OPT_aux_info)) {
+    Res.getFrontendOpts().OutputFile = A->getValue();
+  }
+
   return Success;
 }
 
diff --git a/lib/Frontend/FrontendActions.cpp b/lib/Frontend/FrontendActions.cpp
index 060cec23..1d0044c8 100644
--- a/lib/Frontend/FrontendActions.cpp
+++ b/lib/Frontend/FrontendActions.cpp
@@ -93,6 +93,19 @@ ASTViewAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
   return CreateASTViewer();
 }
 
+std::unique_ptr<ASTConsumer>
+ASTFunctionPrinterAction::CreateASTConsumer(CompilerInstance &CI,
+                                            StringRef InFile) {
+  StringRef OutputFileName = CI.getFrontendOpts().OutputFile;
+  std::unique_ptr<llvm::raw_fd_ostream> OutFile;
+  if (!OutputFileName.empty() && OutputFileName != "-") {
+    std::error_code EC;
+    OutFile.reset(new llvm::raw_fd_ostream(OutputFileName.str(), EC,
+                                           llvm::sys::fs::CD_CreateAlways));
+  }
+  return CreateASTFunctionPrinter(std::move(OutFile));
+}
+
 std::unique_ptr<ASTConsumer>
 GeneratePCHAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
   std::string Sysroot;
diff --git a/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/lib/FrontendTool/ExecuteCompilerInvocation.cpp
index ac64e170..63a6b403 100644
--- a/lib/FrontendTool/ExecuteCompilerInvocation.cpp
+++ b/lib/FrontendTool/ExecuteCompilerInvocation.cpp
@@ -46,6 +46,8 @@ CreateFrontendBaseAction(CompilerInstance &CI) {
   case ASTDump:                return std::make_unique<ASTDumpAction>();
   case ASTPrint:               return std::make_unique<ASTPrintAction>();
   case ASTView:                return std::make_unique<ASTViewAction>();
+  case ASTFunctionPrinter:
+    return std::make_unique<ASTFunctionPrinterAction>();
   case DumpCompilerOptions:
     return std::make_unique<DumpCompilerOptionsAction>();
   case DumpRawTokens:          return std::make_unique<DumpRawTokensAction>();
diff --git a/test/Frontend/print-function-prototype.c b/test/Frontend/print-function-prototype.c
new file mode 100644
index 00000000..5abc7550
--- /dev/null
+++ b/test/Frontend/print-function-prototype.c
@@ -0,0 +1,28 @@
+//  Test that -aux-info prints correct function prototypes.
+// RUN: %clang %s -aux-info %t 2>&1 \
+// RUN:   | FileCheck -input-file=%t %s
+// CHECK: extern void function1(int, const int, float);
+// CHECK: extern int function2(int, int);
+// CHECK: extern void function3(int, double, float);
+// CHECK: static void function4(void);
+// CHECK: extern int function5(volatile int *);
+// CHECK: extern int function6(int *);
+// CHECK: extern const int function7(void);
+// CHECK: extern volatile float function8(void);
+// CHECK: extern int main(void);
+// CHECK: extern void function1(int, const int, float);
+
+void function1(int, const int, float);
+int function2(int a, int b) { return a + b; }
+void function3(int a, double b, float c) {}
+static void function4() {}
+inline int function5(volatile int *j);
+int function6(int *a) { return 0; }
+const int function7() { return 1; }
+volatile float function8() { return 0.; }
+int main() {
+  function1(1, 1, 0.0);
+  function2(1, 1);
+  return 0;
+}
+void function1(int a, const int b, float c) {}
\ No newline at end of file
-- 
2.25.1

