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

0001 // ct_lvtclp_diagnostic_consumer.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_diagnostic_consumer.h>
0021 #include <ct_lvtmdb_objectstore.h>
0022 #include <ct_lvtmdb_util.h>
0023 
0024 #include <iostream>
0025 #include <memory>
0026 #include <thread>
0027 
0028 #include <llvm/ADT/SmallString.h>
0029 #include <llvm/Support/raw_ostream.h>
0030 
0031 #include <clang/Basic/LangOptions.h>
0032 #include <clang/Frontend/TextDiagnostic.h>
0033 
0034 using namespace Codethink::lvtclp;
0035 
0036 struct DiagnosticConsumer::Private {
0037     lvtmdb::ObjectStore& memDb;
0038     std::function<void(std::string, long)> messageCallback;
0039 
0040     clang::LangOptions langOpts;
0041 
0042     explicit Private(lvtmdb::ObjectStore& memDb, std::function<void(std::string, long)> messageCallback):
0043         memDb(memDb), messageCallback(std::move(messageCallback))
0044     {
0045     }
0046 };
0047 
0048 DiagnosticConsumer::DiagnosticConsumer(lvtmdb::ObjectStore& memDb,
0049                                        std::function<void(std::string, long)> messageCallback):
0050     d(std::make_unique<Private>(memDb, std::move(messageCallback)))
0051 {
0052 }
0053 
0054 DiagnosticConsumer::~DiagnosticConsumer() = default;
0055 
0056 void DiagnosticConsumer::HandleDiagnostic(clang::DiagnosticsEngine::Level DiagLevel, const clang::Diagnostic& Info)
0057 {
0058     // maintain error and warning counts
0059     clang::DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info);
0060 
0061     using Level = clang::DiagnosticsEngine::Level;
0062 
0063     // don't print warnings - the output is already too busy
0064     if (DiagLevel != Level::Error && DiagLevel != Level::Fatal) {
0065         return;
0066     }
0067 
0068     llvm::SmallString<1024> message;
0069     Info.FormatDiagnostic(message);
0070 
0071     long threadId = static_cast<long>(std::hash<std::thread::id>{}(std::this_thread::get_id()));
0072 
0073     assert(Info.hasSourceManager());
0074 
0075     std::string out;
0076     llvm::raw_string_ostream ss(out);
0077     clang::TextDiagnostic textDiag(ss, d->langOpts, new clang::DiagnosticOptions());
0078 
0079     textDiag.emitDiagnostic(clang::FullSourceLoc(Info.getLocation(), Info.getSourceManager()),
0080                             DiagLevel,
0081                             message,
0082                             Info.getRanges(),
0083                             Info.getFixItHints());
0084 
0085     d->messageCallback(ss.str(), threadId);
0086 
0087     d->memDb.withRWLock([&] {
0088         d->memDb.getOrAddError(
0089             lvtmdb::MdbUtil::ErrorKind::CompilerError,
0090             "", // There's no need to store the qualified name for a compiler error. we don't know the qualified name.
0091             ss.str(),
0092             Info.getLocation().printToString(Info.getSourceManager()));
0093     });
0094 }