File indexing completed on 2024-05-19 05:42:02

0001 // ct_lvtclp_logicaldepscanner.cpp                                    -*-C++-*-
0002 
0003 /*
0004 // Copyright 2023 Codethink Ltd <codethink@codethink.co.uk>
0005 // SPDX-License-Identifier: Apache-2.0
0006 //
0007 // Licensed under the Apache License, Version 2.0 (the "License");
0008 // you may not use this file except in compliance with the License.
0009 // You may obtain a copy of the License at
0010 //
0011 //     http://www.apache.org/licenses/LICENSE-2.0
0012 //
0013 // Unless required by applicable law or agreed to in writing, software
0014 // distributed under the License is distributed on an "AS IS" BASIS,
0015 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0016 // See the License for the specific language governing permissions and
0017 // limitations under the License.
0018 */
0019 
0020 #include <ct_lvtclp_logicaldepscanner.h>
0021 #include <ct_lvtclp_logicaldepvisitor.h>
0022 
0023 #include <ct_lvtclp_staticfnhandler.h>
0024 #include <ct_lvtclp_visitlog.h>
0025 
0026 #include <clang/AST/ASTConsumer.h>
0027 #include <clang/Frontend/CompilerInstance.h>
0028 #include <clang/Frontend/FrontendActions.h>
0029 
0030 #include <ct_lvtclp_diagnostic_consumer.h>
0031 
0032 #include <filesystem>
0033 
0034 namespace {
0035 
0036 using namespace Codethink;
0037 using namespace lvtclp;
0038 
0039 // ========================
0040 // class LogicalDepConsumer
0041 // ========================
0042 
0043 class LogicalDepConsumer : public clang::ASTConsumer {
0044     // Implementation of the ASTConsumer interface for reading an AST produced
0045     // by the Clang parser. Each instance is specific to a file.
0046 
0047     // DATA
0048     LogicalDepVisitor d_visitor;
0049     // LogicalDepVisitor which inspects AST nodes
0050 
0051     std::optional<std::function<void(const std::string&, long)>> d_messageCallback;
0052     // sends a message to the UI.
0053 
0054     std::optional<HandleCppCommentsCallback_f> d_handleCppCommentsCallback;
0055 
0056     std::string d_filename;
0057     clang::ASTContext *d_context;
0058 
0059   public:
0060     // CREATORS
0061     LogicalDepConsumer(clang::ASTContext *context,
0062                        clang::StringRef file,
0063                        lvtmdb::ObjectStore& memDb,
0064                        const std::filesystem::path& prefix,
0065                        const std::vector<std::filesystem::path>& nonLakosians,
0066                        std::vector<std::pair<std::string, std::string>> d_thirdPartyDirs,
0067                        const std::shared_ptr<VisitLog>& visitLog,
0068                        const std::shared_ptr<StaticFnHandler>& staticFnHandler,
0069                        std::optional<std::function<void(const std::string&, long)>> messageCallback,
0070                        bool catchCodeAnalysisOutput,
0071                        std::optional<HandleCppCommentsCallback_f> handleCppCommentsCallback = std::nullopt):
0072 
0073         // Instantiates a new LogicalDepConsumer for the given file as a
0074         // translation unit
0075         d_visitor(context,
0076                   file,
0077                   memDb,
0078                   prefix,
0079                   nonLakosians,
0080                   std::move(d_thirdPartyDirs),
0081                   visitLog,
0082                   staticFnHandler,
0083                   std::move(messageCallback),
0084                   catchCodeAnalysisOutput),
0085         d_handleCppCommentsCallback(std::move(handleCppCommentsCallback)),
0086         d_filename(file.str()),
0087         d_context(context)
0088     {
0089     }
0090 
0091     // MANIPULATORS
0092     bool HandleTopLevelDecl(clang::DeclGroupRef declGroupRef) override
0093     // Override the method that gets called for each parsed top-level
0094     // declaration
0095     {
0096         if (d_handleCppCommentsCallback) {
0097             auto& ctx = *d_context;
0098             auto& sm = ctx.getSourceManager();
0099             auto *commentsInFile = ctx.Comments.getCommentsInFile(sm.getMainFileID());
0100             if (commentsInFile) {
0101                 for (auto&& [_, rawComment] : *commentsInFile) {
0102                     auto filename = std::filesystem::path{d_filename}.filename().string();
0103                     auto briefText = rawComment->getBriefText(ctx);
0104                     auto startLine = sm.getExpansionLineNumber(rawComment->getBeginLoc());
0105                     auto endLine = sm.getExpansionLineNumber(rawComment->getEndLoc());
0106                     (*d_handleCppCommentsCallback)(filename, briefText, startLine, endLine);
0107                 }
0108             }
0109         }
0110 
0111         for (auto *b : declGroupRef) {
0112             // Traverse the declaration using our AST visitor.
0113             d_visitor.TraverseDecl(b);
0114         }
0115 
0116         return true;
0117     }
0118 };
0119 
0120 // ==============================
0121 // class LogicalDepFrontendAction
0122 // ==============================
0123 
0124 class LogicalDepFrontendAction : public clang::SyntaxOnlyAction {
0125     // Per-thread clang tooling stuff
0126 
0127     // DATA
0128     lvtmdb::ObjectStore& d_memDb;
0129 
0130     std::filesystem::path d_prefix;
0131 
0132     std::vector<std::filesystem::path> d_nonLakosianDirs;
0133     std::vector<std::pair<std::string, std::string>> d_thirdPartyDirs;
0134 
0135     std::shared_ptr<VisitLog> d_visitLog_p;
0136     std::shared_ptr<StaticFnHandler> d_staticFnHandler_p;
0137 
0138     std::function<void(const std::string&)> d_filenameCallback;
0139     // callback when we process a new file
0140 
0141     std::optional<std::function<void(const std::string&, long)>> d_messageCallback;
0142     // sends a message to the UI.
0143 
0144     bool d_catchCodeAnalysisOutput;
0145 
0146     std::optional<HandleCppCommentsCallback_f> d_handleCppCommentsCallback;
0147 
0148   public:
0149     // CREATORS
0150     LogicalDepFrontendAction(lvtmdb::ObjectStore& memDb,
0151                              std::filesystem::path prefix,
0152                              std::vector<std::filesystem::path> nonLakosians,
0153                              std::vector<std::pair<std::string, std::string>> thirdPartyDirs,
0154                              std::function<void(const std::string&)> filenameCallback,
0155                              std::optional<std::function<void(const std::string&, long)>> messageCallback,
0156                              bool catchCodeAnalysisOutput,
0157                              std::optional<HandleCppCommentsCallback_f> handleCppCommentsCallback = std::nullopt):
0158         d_memDb(memDb),
0159         d_prefix(std::move(prefix)),
0160         d_nonLakosianDirs(std::move(nonLakosians)),
0161         d_thirdPartyDirs(std::move(thirdPartyDirs)),
0162         d_visitLog_p(std::make_shared<VisitLog>()),
0163         d_staticFnHandler_p(std::make_shared<StaticFnHandler>(memDb)),
0164         d_filenameCallback(std::move(filenameCallback)),
0165         d_messageCallback(std::move(messageCallback)),
0166         d_catchCodeAnalysisOutput(catchCodeAnalysisOutput),
0167         d_handleCppCommentsCallback(std::move(handleCppCommentsCallback))
0168     {
0169     }
0170 
0171     // MANIPULATORS
0172     std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(clang::CompilerInstance& compiler,
0173                                                           clang::StringRef file) override
0174     {
0175         // try our best not to bail on compilation errors
0176         clang::FrontendOptions& fOpts = compiler.getFrontendOpts();
0177         fOpts.FixWhatYouCan = true;
0178         fOpts.FixAndRecompile = true;
0179         fOpts.FixToTemporaries = true;
0180 
0181         // Attempt to obliterate any -Werror options
0182         clang::DiagnosticOptions& dOpts = compiler.getDiagnosticOpts();
0183         dOpts.Warnings = {"-Wno-everything"};
0184 
0185         return std::make_unique<LogicalDepConsumer>(&compiler.getASTContext(),
0186                                                     file,
0187                                                     d_memDb,
0188                                                     d_prefix,
0189                                                     d_nonLakosianDirs,
0190                                                     d_thirdPartyDirs,
0191                                                     d_visitLog_p,
0192                                                     d_staticFnHandler_p,
0193                                                     d_messageCallback,
0194                                                     d_catchCodeAnalysisOutput,
0195                                                     d_handleCppCommentsCallback);
0196     }
0197 
0198     bool BeginSourceFileAction(clang::CompilerInstance& ci) override
0199     {
0200         d_filenameCallback(getCurrentFile().str());
0201 
0202         return SyntaxOnlyAction::BeginSourceFileAction(ci);
0203     }
0204 
0205     void EndSourceFileAction() override
0206     // Callback at the end of processing a single input
0207     {
0208         d_staticFnHandler_p->writeOutToDb();
0209         d_staticFnHandler_p->reset();
0210     }
0211 };
0212 
0213 } // namespace
0214 
0215 namespace Codethink::lvtclp {
0216 
0217 LogicalDepActionFactory::LogicalDepActionFactory(
0218     lvtmdb::ObjectStore& memDb,
0219     std::filesystem::path prefix,
0220     std::vector<std::filesystem::path> nonLakosians,
0221     std::vector<std::pair<std::string, std::string>> thirdPartyDirs,
0222     std::function<void(const std::string&)> filenameCallback,
0223     std::optional<std::function<void(const std::string&, long)>> messageCallback,
0224     bool catchCodeAnalysisOutput,
0225     std::optional<HandleCppCommentsCallback_f> handleCppCommentsCallback):
0226     d_memDb(memDb),
0227     d_prefix(std::move(prefix)),
0228     d_nonLakosianDirs(std::move(nonLakosians)),
0229     d_thirdPartyDirs(std::move(thirdPartyDirs)),
0230     d_filenameCallback(std::move(filenameCallback)),
0231     d_messageCallback(std::move(messageCallback)),
0232     d_catchCodeAnalysisOutput(catchCodeAnalysisOutput),
0233     d_handleCppCommentsCallback(std::move(handleCppCommentsCallback))
0234 {
0235 }
0236 
0237 std::unique_ptr<clang::FrontendAction> LogicalDepActionFactory::create()
0238 {
0239     return std::make_unique<LogicalDepFrontendAction>(d_memDb,
0240                                                       d_prefix,
0241                                                       d_nonLakosianDirs,
0242                                                       d_thirdPartyDirs,
0243                                                       d_filenameCallback,
0244                                                       d_messageCallback,
0245                                                       d_catchCodeAnalysisOutput,
0246                                                       d_handleCppCommentsCallback);
0247 }
0248 
0249 } // namespace Codethink::lvtclp