|
21 | 21 | #include "ClangTidyProfiling.h"
|
22 | 22 | #include "ExpandModularHeadersPPCallbacks.h"
|
23 | 23 | #include "clang-tidy-config.h"
|
| 24 | +#include "utils/OptionsUtils.h" |
24 | 25 | #include "clang/AST/ASTConsumer.h"
|
25 | 26 | #include "clang/ASTMatchers/ASTMatchFinder.h"
|
26 | 27 | #include "clang/Format/Format.h"
|
|
48 | 49 | #include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h"
|
49 | 50 | #endif // CLANG_TIDY_ENABLE_STATIC_ANALYZER
|
50 | 51 |
|
| 52 | +#if CLANG_ENABLE_CIR |
| 53 | +#include "mlir/IR/BuiltinOps.h" |
| 54 | +#include "mlir/IR/MLIRContext.h" |
| 55 | +#include "mlir/Pass/Pass.h" |
| 56 | +#include "mlir/Pass/PassManager.h" |
| 57 | +#include "clang/AST/ASTContext.h" |
| 58 | +#include "clang/CIR/CIRGenerator.h" |
| 59 | +#include "clang/CIR/Dialect/Passes.h" |
| 60 | +#include <algorithm> |
| 61 | +#endif // CLANG_ENABLE_CIR |
| 62 | + |
51 | 63 | using namespace clang::ast_matchers;
|
52 | 64 | using namespace clang::driver;
|
53 | 65 | using namespace clang::tooling;
|
@@ -92,6 +104,205 @@ class AnalyzerDiagnosticConsumer : public ento::PathDiagnosticConsumer {
|
92 | 104 | };
|
93 | 105 | #endif // CLANG_TIDY_ENABLE_STATIC_ANALYZER
|
94 | 106 |
|
| 107 | +#if CLANG_ENABLE_CIR |
| 108 | +namespace cir { |
| 109 | + |
| 110 | +constexpr const char *LifetimeCheckName = "cir-lifetime-check"; |
| 111 | +struct CIROpts { |
| 112 | + std::vector<StringRef> RemarksList; |
| 113 | + std::vector<StringRef> HistoryList; |
| 114 | + unsigned HistLimit; |
| 115 | +}; |
| 116 | + |
| 117 | +static const char StringsDelimiter[] = ";"; |
| 118 | + |
| 119 | +// FIXME(cir): this function was extracted from clang::tidy::utils::options |
| 120 | +// given that ClangTidy.cpp cannot be linked with ClangTidyUtils. |
| 121 | +std::vector<StringRef> parseStringList(StringRef Option) { |
| 122 | + Option = Option.trim().trim(StringsDelimiter); |
| 123 | + if (Option.empty()) |
| 124 | + return {}; |
| 125 | + std::vector<StringRef> Result; |
| 126 | + Result.reserve(Option.count(StringsDelimiter) + 1); |
| 127 | + StringRef Cur; |
| 128 | + while (std::tie(Cur, Option) = Option.split(StringsDelimiter), |
| 129 | + !Option.empty()) { |
| 130 | + Cur = Cur.trim(); |
| 131 | + if (!Cur.empty()) |
| 132 | + Result.push_back(Cur); |
| 133 | + } |
| 134 | + Cur = Cur.trim(); |
| 135 | + if (!Cur.empty()) |
| 136 | + Result.push_back(Cur); |
| 137 | + return Result; |
| 138 | +} |
| 139 | + |
| 140 | +class CIRASTConsumer : public ASTConsumer { |
| 141 | +public: |
| 142 | + CIRASTConsumer(CompilerInstance &CI, StringRef inputFile, |
| 143 | + clang::tidy::ClangTidyContext &Context, CIROpts &cirOpts); |
| 144 | + |
| 145 | +private: |
| 146 | + void Initialize(ASTContext &Context) override; |
| 147 | + void HandleTranslationUnit(ASTContext &C) override; |
| 148 | + bool HandleTopLevelDecl(DeclGroupRef D) override; |
| 149 | + std::unique_ptr<::cir::CIRGenerator> Gen; |
| 150 | + ASTContext *AstContext{nullptr}; |
| 151 | + clang::tidy::ClangTidyContext &Context; |
| 152 | + CIROpts cirOpts; |
| 153 | +}; |
| 154 | + |
| 155 | +/// CIR AST Consumer |
| 156 | +CIRASTConsumer::CIRASTConsumer(CompilerInstance &CI, StringRef inputFile, |
| 157 | + clang::tidy::ClangTidyContext &Context, |
| 158 | + CIROpts &O) |
| 159 | + : Context(Context), cirOpts(O) { |
| 160 | + Gen = std::make_unique<::cir::CIRGenerator>(CI.getDiagnostics(), nullptr, |
| 161 | + CI.getCodeGenOpts()); |
| 162 | +} |
| 163 | + |
| 164 | +bool CIRASTConsumer::HandleTopLevelDecl(DeclGroupRef D) { |
| 165 | + PrettyStackTraceDecl CrashInfo(*D.begin(), SourceLocation(), |
| 166 | + AstContext->getSourceManager(), |
| 167 | + "CIR generation of declaration"); |
| 168 | + Gen->HandleTopLevelDecl(D); |
| 169 | + return true; |
| 170 | +} |
| 171 | + |
| 172 | +void CIRASTConsumer::Initialize(ASTContext &Context) { |
| 173 | + AstContext = &Context; |
| 174 | + Gen->Initialize(Context); |
| 175 | +} |
| 176 | + |
| 177 | +void CIRASTConsumer::HandleTranslationUnit(ASTContext &C) { |
| 178 | + Gen->HandleTranslationUnit(C); |
| 179 | + Gen->verifyModule(); |
| 180 | + |
| 181 | + mlir::ModuleOp mlirMod = Gen->getModule(); |
| 182 | + std::unique_ptr<mlir::MLIRContext> mlirCtx = Gen->takeContext(); |
| 183 | + |
| 184 | + mlir::OpPrintingFlags flags; |
| 185 | + flags.enableDebugInfo(/*prettyForm=*/false); |
| 186 | + |
| 187 | + clang::SourceManager &clangSrcMgr = C.getSourceManager(); |
| 188 | + FileID MainFileID = clangSrcMgr.getMainFileID(); |
| 189 | + |
| 190 | + llvm::MemoryBufferRef MainFileBuf = clangSrcMgr.getBufferOrFake(MainFileID); |
| 191 | + std::unique_ptr<llvm::MemoryBuffer> FileBuf = |
| 192 | + llvm::MemoryBuffer::getMemBuffer(MainFileBuf); |
| 193 | + |
| 194 | + llvm::SourceMgr llvmSrcMgr; |
| 195 | + llvmSrcMgr.AddNewSourceBuffer(std::move(FileBuf), llvm::SMLoc()); |
| 196 | + |
| 197 | + class CIRTidyDiagnosticHandler : public mlir::SourceMgrDiagnosticHandler { |
| 198 | + clang::tidy::ClangTidyContext &tidyCtx; |
| 199 | + clang::SourceManager &clangSrcMgr; |
| 200 | + |
| 201 | + clang::SourceLocation getClangFromFileLineCol(mlir::FileLineColLoc loc) { |
| 202 | + clang::SourceLocation clangLoc; |
| 203 | + FileManager &fileMgr = clangSrcMgr.getFileManager(); |
| 204 | + assert(loc && "not a valid mlir::FileLineColLoc"); |
| 205 | + // The column and line may be zero to represent unknown column and/or |
| 206 | + // unknown line/column information. |
| 207 | + if (loc.getLine() == 0 || loc.getColumn() == 0) { |
| 208 | + llvm_unreachable("How should we workaround this?"); |
| 209 | + return clangLoc; |
| 210 | + } |
| 211 | + if (auto FE = fileMgr.getFile(loc.getFilename())) { |
| 212 | + return clangSrcMgr.translateFileLineCol(*FE, loc.getLine(), |
| 213 | + loc.getColumn()); |
| 214 | + } |
| 215 | + llvm_unreachable("location doesn't map to a file?"); |
| 216 | + } |
| 217 | + |
| 218 | + clang::SourceLocation getClangSrcLoc(mlir::Location loc) { |
| 219 | + // Direct maps into a clang::SourceLocation. |
| 220 | + if (auto fileLoc = loc.dyn_cast<mlir::FileLineColLoc>()) { |
| 221 | + return getClangFromFileLineCol(fileLoc); |
| 222 | + } |
| 223 | + |
| 224 | + // FusedLoc needs to be decomposed but the canonical one |
| 225 | + // is the first location, we handle source ranges somewhere |
| 226 | + // else. |
| 227 | + if (auto fileLoc = loc.dyn_cast<mlir::FusedLoc>()) { |
| 228 | + auto locArray = fileLoc.getLocations(); |
| 229 | + assert(locArray.size() > 0 && "expected multiple locs"); |
| 230 | + return getClangFromFileLineCol( |
| 231 | + locArray[0].dyn_cast<mlir::FileLineColLoc>()); |
| 232 | + } |
| 233 | + |
| 234 | + // Many loc styles are yet to be handled. |
| 235 | + if (auto fileLoc = loc.dyn_cast<mlir::UnknownLoc>()) { |
| 236 | + llvm_unreachable("mlir::UnknownLoc not implemented!"); |
| 237 | + } |
| 238 | + if (auto fileLoc = loc.dyn_cast<mlir::CallSiteLoc>()) { |
| 239 | + llvm_unreachable("mlir::CallSiteLoc not implemented!"); |
| 240 | + } |
| 241 | + llvm_unreachable("Unknown location style"); |
| 242 | + } |
| 243 | + |
| 244 | + clang::DiagnosticIDs::Level |
| 245 | + translateToClangDiagLevel(const mlir::DiagnosticSeverity &sev) { |
| 246 | + switch (sev) { |
| 247 | + case mlir::DiagnosticSeverity::Note: |
| 248 | + return clang::DiagnosticIDs::Level::Note; |
| 249 | + case mlir::DiagnosticSeverity::Warning: |
| 250 | + return clang::DiagnosticIDs::Level::Warning; |
| 251 | + case mlir::DiagnosticSeverity::Error: |
| 252 | + return clang::DiagnosticIDs::Level::Error; |
| 253 | + case mlir::DiagnosticSeverity::Remark: |
| 254 | + return clang::DiagnosticIDs::Level::Remark; |
| 255 | + } |
| 256 | + llvm_unreachable("should not get here!"); |
| 257 | + } |
| 258 | + |
| 259 | + public: |
| 260 | + void emitClangTidyDiagnostic(mlir::Diagnostic &diag) { |
| 261 | + auto clangBeginLoc = getClangSrcLoc(diag.getLocation()); |
| 262 | + tidyCtx.diag(LifetimeCheckName, clangBeginLoc, diag.str(), |
| 263 | + translateToClangDiagLevel(diag.getSeverity())); |
| 264 | + for (const auto ¬e : diag.getNotes()) { |
| 265 | + auto clangNoteBeginLoc = getClangSrcLoc(note.getLocation()); |
| 266 | + tidyCtx.diag(LifetimeCheckName, clangNoteBeginLoc, note.str(), |
| 267 | + translateToClangDiagLevel(note.getSeverity())); |
| 268 | + } |
| 269 | + } |
| 270 | + |
| 271 | + CIRTidyDiagnosticHandler(llvm::SourceMgr &mgr, mlir::MLIRContext *ctx, |
| 272 | + clang::tidy::ClangTidyContext &tidyContext, |
| 273 | + clang::SourceManager &clangMgr, |
| 274 | + ShouldShowLocFn &&shouldShowLocFn = {}) |
| 275 | + : SourceMgrDiagnosticHandler(mgr, ctx, llvm::errs(), |
| 276 | + std::move(shouldShowLocFn)), |
| 277 | + tidyCtx(tidyContext), clangSrcMgr(clangMgr) { |
| 278 | + setHandler( |
| 279 | + [this](mlir::Diagnostic &diag) { emitClangTidyDiagnostic(diag); }); |
| 280 | + } |
| 281 | + ~CIRTidyDiagnosticHandler() = default; |
| 282 | + }; |
| 283 | + |
| 284 | + // Use a custom diagnostic handler that can allow both regular printing to |
| 285 | + // stderr but also populates clang-tidy context with diagnostics (and allow |
| 286 | + // for instance, diagnostics to be later converted to YAML). |
| 287 | + CIRTidyDiagnosticHandler sourceMgrHandler(llvmSrcMgr, mlirCtx.get(), Context, |
| 288 | + clangSrcMgr); |
| 289 | + |
| 290 | + mlir::PassManager pm(mlirCtx.get()); |
| 291 | + pm.addPass(mlir::createMergeCleanupsPass()); |
| 292 | + |
| 293 | + if (Context.isCheckEnabled(LifetimeCheckName)) |
| 294 | + pm.addPass(mlir::createLifetimeCheckPass( |
| 295 | + cirOpts.RemarksList, cirOpts.HistoryList, cirOpts.HistLimit, &C)); |
| 296 | + |
| 297 | + bool Result = !mlir::failed(pm.run(mlirMod)); |
| 298 | + if (!Result) |
| 299 | + llvm::report_fatal_error( |
| 300 | + "The pass manager failed to run pass on the module!"); |
| 301 | +} |
| 302 | +} // namespace cir |
| 303 | + |
| 304 | +#endif |
| 305 | + |
95 | 306 | class ErrorReporter {
|
96 | 307 | public:
|
97 | 308 | ErrorReporter(ClangTidyContext &Context, FixBehaviour ApplyFixes,
|
@@ -449,6 +660,28 @@ ClangTidyASTConsumerFactory::createASTConsumer(
|
449 | 660 | Consumers.push_back(std::move(AnalysisConsumer));
|
450 | 661 | }
|
451 | 662 | #endif // CLANG_TIDY_ENABLE_STATIC_ANALYZER
|
| 663 | + |
| 664 | +#if CLANG_ENABLE_CIR |
| 665 | + if (Context.isCheckEnabled(cir::LifetimeCheckName)) { |
| 666 | + auto OV = ClangTidyCheck::OptionsView( |
| 667 | + cir::LifetimeCheckName, Context.getOptions().CheckOptions, &Context); |
| 668 | + // Setup CIR codegen options via config specified information. |
| 669 | + Compiler.getCodeGenOpts().ClangIRBuildDeferredThreshold = |
| 670 | + OV.get("CodeGenBuildDeferredThreshold", 500U); |
| 671 | + Compiler.getCodeGenOpts().ClangIRSkipFunctionsFromSystemHeaders = |
| 672 | + OV.get("CodeGenSkipFunctionsFromSystemHeaders", false); |
| 673 | + |
| 674 | + cir::CIROpts opts; |
| 675 | + opts.RemarksList = cir::parseStringList(OV.get("RemarksList", "")); |
| 676 | + opts.HistoryList = cir::parseStringList(OV.get("HistoryList", "all")); |
| 677 | + opts.HistLimit = OV.get("HistLimit", 1U); |
| 678 | + |
| 679 | + std::unique_ptr<cir::CIRASTConsumer> CIRConsumer = |
| 680 | + std::make_unique<cir::CIRASTConsumer>(Compiler, File, Context, opts); |
| 681 | + Consumers.push_back(std::move(CIRConsumer)); |
| 682 | + } |
| 683 | +#endif // CLANG_ENABLE_CIR |
| 684 | + |
452 | 685 | return std::make_unique<ClangTidyASTConsumer>(
|
453 | 686 | std::move(Consumers), std::move(Profiling), std::move(Finder),
|
454 | 687 | std::move(Checks));
|
|
0 commit comments