1 //===--- FrontendActions.cpp ----------------------------------------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "clang/Rewrite/Frontend/FrontendActions.h" 11 #include "clang/AST/ASTConsumer.h" 12 #include "clang/Frontend/CompilerInstance.h" 13 #include "clang/Frontend/FrontendActions.h" 14 #include "clang/Frontend/FrontendDiagnostic.h" 15 #include "clang/Frontend/Utils.h" 16 #include "clang/Lex/Preprocessor.h" 17 #include "clang/Lex/PreprocessorOptions.h" 18 #include "clang/Rewrite/Frontend/ASTConsumers.h" 19 #include "clang/Rewrite/Frontend/FixItRewriter.h" 20 #include "clang/Rewrite/Frontend/Rewriters.h" 21 #include "clang/Serialization/ASTReader.h" 22 #include "clang/Serialization/Module.h" 23 #include "clang/Serialization/ModuleManager.h" 24 #include "llvm/ADT/DenseSet.h" 25 #include "llvm/Support/CrashRecoveryContext.h" 26 #include "llvm/Support/FileSystem.h" 27 #include "llvm/Support/Path.h" 28 #include "llvm/Support/raw_ostream.h" 29 #include <memory> 30 #include <utility> 31 32 using namespace clang; 33 34 //===----------------------------------------------------------------------===// 35 // AST Consumer Actions 36 //===----------------------------------------------------------------------===// 37 38 std::unique_ptr<ASTConsumer> 39 HTMLPrintAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { 40 if (std::unique_ptr<raw_ostream> OS = 41 CI.createDefaultOutputFile(false, InFile)) 42 return CreateHTMLPrinter(std::move(OS), CI.getPreprocessor()); 43 return nullptr; 44 } 45 46 FixItAction::FixItAction() {} 47 FixItAction::~FixItAction() {} 48 49 std::unique_ptr<ASTConsumer> 50 FixItAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { 51 return llvm::make_unique<ASTConsumer>(); 52 } 53 54 namespace { 55 class FixItRewriteInPlace : public FixItOptions { 56 public: 57 FixItRewriteInPlace() { InPlace = true; } 58 59 std::string RewriteFilename(const std::string &Filename, int &fd) override { 60 llvm_unreachable("don't call RewriteFilename for inplace rewrites"); 61 } 62 }; 63 64 class FixItActionSuffixInserter : public FixItOptions { 65 std::string NewSuffix; 66 67 public: 68 FixItActionSuffixInserter(std::string NewSuffix, bool FixWhatYouCan) 69 : NewSuffix(std::move(NewSuffix)) { 70 this->FixWhatYouCan = FixWhatYouCan; 71 } 72 73 std::string RewriteFilename(const std::string &Filename, int &fd) override { 74 fd = -1; 75 SmallString<128> Path(Filename); 76 llvm::sys::path::replace_extension(Path, 77 NewSuffix + llvm::sys::path::extension(Path)); 78 return Path.str(); 79 } 80 }; 81 82 class FixItRewriteToTemp : public FixItOptions { 83 public: 84 std::string RewriteFilename(const std::string &Filename, int &fd) override { 85 SmallString<128> Path; 86 llvm::sys::fs::createTemporaryFile(llvm::sys::path::filename(Filename), 87 llvm::sys::path::extension(Filename).drop_front(), fd, 88 Path); 89 return Path.str(); 90 } 91 }; 92 } // end anonymous namespace 93 94 bool FixItAction::BeginSourceFileAction(CompilerInstance &CI) { 95 const FrontendOptions &FEOpts = getCompilerInstance().getFrontendOpts(); 96 if (!FEOpts.FixItSuffix.empty()) { 97 FixItOpts.reset(new FixItActionSuffixInserter(FEOpts.FixItSuffix, 98 FEOpts.FixWhatYouCan)); 99 } else { 100 FixItOpts.reset(new FixItRewriteInPlace); 101 FixItOpts->FixWhatYouCan = FEOpts.FixWhatYouCan; 102 } 103 Rewriter.reset(new FixItRewriter(CI.getDiagnostics(), CI.getSourceManager(), 104 CI.getLangOpts(), FixItOpts.get())); 105 return true; 106 } 107 108 void FixItAction::EndSourceFileAction() { 109 // Otherwise rewrite all files. 110 Rewriter->WriteFixedFiles(); 111 } 112 113 bool FixItRecompile::BeginInvocation(CompilerInstance &CI) { 114 115 std::vector<std::pair<std::string, std::string> > RewrittenFiles; 116 bool err = false; 117 { 118 const FrontendOptions &FEOpts = CI.getFrontendOpts(); 119 std::unique_ptr<FrontendAction> FixAction(new SyntaxOnlyAction()); 120 if (FixAction->BeginSourceFile(CI, FEOpts.Inputs[0])) { 121 std::unique_ptr<FixItOptions> FixItOpts; 122 if (FEOpts.FixToTemporaries) 123 FixItOpts.reset(new FixItRewriteToTemp()); 124 else 125 FixItOpts.reset(new FixItRewriteInPlace()); 126 FixItOpts->Silent = true; 127 FixItOpts->FixWhatYouCan = FEOpts.FixWhatYouCan; 128 FixItOpts->FixOnlyWarnings = FEOpts.FixOnlyWarnings; 129 FixItRewriter Rewriter(CI.getDiagnostics(), CI.getSourceManager(), 130 CI.getLangOpts(), FixItOpts.get()); 131 FixAction->Execute(); 132 133 err = Rewriter.WriteFixedFiles(&RewrittenFiles); 134 135 FixAction->EndSourceFile(); 136 CI.setSourceManager(nullptr); 137 CI.setFileManager(nullptr); 138 } else { 139 err = true; 140 } 141 } 142 if (err) 143 return false; 144 CI.getDiagnosticClient().clear(); 145 CI.getDiagnostics().Reset(); 146 147 PreprocessorOptions &PPOpts = CI.getPreprocessorOpts(); 148 PPOpts.RemappedFiles.insert(PPOpts.RemappedFiles.end(), 149 RewrittenFiles.begin(), RewrittenFiles.end()); 150 PPOpts.RemappedFilesKeepOriginalName = false; 151 152 return true; 153 } 154 155 #ifdef CLANG_ENABLE_OBJC_REWRITER 156 157 std::unique_ptr<ASTConsumer> 158 RewriteObjCAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { 159 if (std::unique_ptr<raw_ostream> OS = 160 CI.createDefaultOutputFile(false, InFile, "cpp")) { 161 if (CI.getLangOpts().ObjCRuntime.isNonFragile()) 162 return CreateModernObjCRewriter( 163 InFile, std::move(OS), CI.getDiagnostics(), CI.getLangOpts(), 164 CI.getDiagnosticOpts().NoRewriteMacros, 165 (CI.getCodeGenOpts().getDebugInfo() != codegenoptions::NoDebugInfo)); 166 return CreateObjCRewriter(InFile, std::move(OS), CI.getDiagnostics(), 167 CI.getLangOpts(), 168 CI.getDiagnosticOpts().NoRewriteMacros); 169 } 170 return nullptr; 171 } 172 173 #endif 174 175 //===----------------------------------------------------------------------===// 176 // Preprocessor Actions 177 //===----------------------------------------------------------------------===// 178 179 void RewriteMacrosAction::ExecuteAction() { 180 CompilerInstance &CI = getCompilerInstance(); 181 std::unique_ptr<raw_ostream> OS = 182 CI.createDefaultOutputFile(true, getCurrentFile()); 183 if (!OS) return; 184 185 RewriteMacrosInInput(CI.getPreprocessor(), OS.get()); 186 } 187 188 void RewriteTestAction::ExecuteAction() { 189 CompilerInstance &CI = getCompilerInstance(); 190 std::unique_ptr<raw_ostream> OS = 191 CI.createDefaultOutputFile(false, getCurrentFile()); 192 if (!OS) return; 193 194 DoRewriteTest(CI.getPreprocessor(), OS.get()); 195 } 196 197 class RewriteIncludesAction::RewriteImportsListener : public ASTReaderListener { 198 CompilerInstance &CI; 199 std::weak_ptr<raw_ostream> Out; 200 201 llvm::DenseSet<const FileEntry*> Rewritten; 202 203 public: 204 RewriteImportsListener(CompilerInstance &CI, std::shared_ptr<raw_ostream> Out) 205 : CI(CI), Out(Out) {} 206 207 void visitModuleFile(StringRef Filename, 208 serialization::ModuleKind Kind) override { 209 auto *File = CI.getFileManager().getFile(Filename); 210 assert(File && "missing file for loaded module?"); 211 212 // Only rewrite each module file once. 213 if (!Rewritten.insert(File).second) 214 return; 215 216 serialization::ModuleFile *MF = 217 CI.getModuleManager()->getModuleManager().lookup(File); 218 assert(File && "missing module file for loaded module?"); 219 220 // Not interested in PCH / preambles. 221 if (!MF->isModule()) 222 return; 223 224 auto OS = Out.lock(); 225 assert(OS && "loaded module file after finishing rewrite action?"); 226 227 (*OS) << "#pragma clang module build " << MF->ModuleName << "\n"; 228 229 // Rewrite the contents of the module in a separate compiler instance. 230 CompilerInstance Instance(CI.getPCHContainerOperations(), 231 &CI.getPreprocessor().getPCMCache()); 232 Instance.setInvocation( 233 std::make_shared<CompilerInvocation>(CI.getInvocation())); 234 Instance.createDiagnostics( 235 new ForwardingDiagnosticConsumer(CI.getDiagnosticClient()), 236 /*ShouldOwnClient=*/true); 237 Instance.getFrontendOpts().Inputs.clear(); 238 Instance.getFrontendOpts().Inputs.emplace_back( 239 Filename, InputKind(InputKind::Unknown, InputKind::Precompiled)); 240 // Don't recursively rewrite imports. We handle them all at the top level. 241 Instance.getPreprocessorOutputOpts().RewriteImports = false; 242 243 llvm::CrashRecoveryContext().RunSafelyOnThread([&]() { 244 RewriteIncludesAction Action; 245 Action.OutputStream = OS; 246 Instance.ExecuteAction(Action); 247 }); 248 249 (*OS) << "#pragma clang module endbuild /*" << MF->ModuleName << "*/\n"; 250 } 251 }; 252 253 bool RewriteIncludesAction::BeginSourceFileAction(CompilerInstance &CI) { 254 if (!OutputStream) { 255 OutputStream = CI.createDefaultOutputFile(true, getCurrentFile()); 256 if (!OutputStream) 257 return false; 258 } 259 260 auto &OS = *OutputStream; 261 262 // If we're preprocessing a module map, start by dumping the contents of the 263 // module itself before switching to the input buffer. 264 auto &Input = getCurrentInput(); 265 if (Input.getKind().getFormat() == InputKind::ModuleMap) { 266 if (Input.isFile()) { 267 OS << "# 1 \""; 268 OS.write_escaped(Input.getFile()); 269 OS << "\"\n"; 270 } 271 getCurrentModule()->print(OS); 272 OS << "#pragma clang module contents\n"; 273 } 274 275 // If we're rewriting imports, set up a listener to track when we import 276 // module files. 277 if (CI.getPreprocessorOutputOpts().RewriteImports) { 278 CI.createModuleManager(); 279 CI.getModuleManager()->addListener( 280 llvm::make_unique<RewriteImportsListener>(CI, OutputStream)); 281 } 282 283 return true; 284 } 285 286 void RewriteIncludesAction::ExecuteAction() { 287 CompilerInstance &CI = getCompilerInstance(); 288 289 // If we're rewriting imports, emit the module build output first rather 290 // than switching back and forth (potentially in the middle of a line). 291 if (CI.getPreprocessorOutputOpts().RewriteImports) { 292 std::string Buffer; 293 llvm::raw_string_ostream OS(Buffer); 294 295 RewriteIncludesInInput(CI.getPreprocessor(), &OS, 296 CI.getPreprocessorOutputOpts()); 297 298 (*OutputStream) << OS.str(); 299 } else { 300 RewriteIncludesInInput(CI.getPreprocessor(), OutputStream.get(), 301 CI.getPreprocessorOutputOpts()); 302 } 303 304 OutputStream.reset(); 305 } 306