diff --git a/.clang-tidy b/.clang-tidy index 7bdf38f1a..63644ff5d 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,38 +1,21 @@ --- -Checks: 'clang-diagnostic-*,clang-analyzer-*' -WarningsAsErrors: '' -HeaderFilterRegex: '' -AnalyzeTemporaryDtors: false -FormatStyle: none -User: andreas -CheckOptions: - - key: cert-dcl16-c.NewSuffixes - value: 'L;LL;LU;LLU' - - key: cert-oop54-cpp.WarnOnlyIfThisHasSuspiciousField - value: '0' - - key: cppcoreguidelines-explicit-virtual-functions.IgnoreDestructors - value: '1' - - key: cppcoreguidelines-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic - value: '1' - - key: google-readability-braces-around-statements.ShortStatementLines - value: '1' - - key: google-readability-function-size.StatementThreshold - value: '800' - - key: google-readability-namespace-comments.ShortNamespaceLines - value: '10' - - key: google-readability-namespace-comments.SpacesBeforeComments - value: '2' - - key: modernize-loop-convert.MaxCopySize - value: '16' - - key: modernize-loop-convert.MinConfidence - value: reasonable - - key: modernize-loop-convert.NamingStyle - value: CamelCase - - key: modernize-pass-by-value.IncludeStyle - value: llvm - - key: modernize-replace-auto-ptr.IncludeStyle - value: llvm - - key: modernize-use-nullptr.NullMacros - value: 'NULL' -... - +# High-signal static analysis. The bug-finding groups are enabled wholesale; +# the negative entries switch off checks that conflict with deliberate project +# conventions (#pragma once) or that are pure style churn / very high false +# positive rate. Everything left enabled is expected to stay at zero findings, +# which WarningsAsErrors enforces. +Checks: > + -*, + bugprone-*, + clang-analyzer-*, + performance-*, + portability-*, + -bugprone-easily-swappable-parameters, + -bugprone-narrowing-conversions, + -performance-enum-size, + -portability-avoid-pragma-once, +# clang-analyzer-* stays enabled but advisory: its only findings are inside +# third-party -isystem headers (cryptopp/httplib/csv), which we cannot patch. +WarningsAsErrors: '*,-clang-analyzer-*' +HeaderFilterRegex: '(^|/)src/odr/' +FormatStyle: file diff --git a/cli/src/back_translate.cpp b/cli/src/back_translate.cpp index eefee80b8..df27b2525 100644 --- a/cli/src/back_translate.cpp +++ b/cli/src/back_translate.cpp @@ -4,31 +4,37 @@ #include +#include #include using namespace odr; int main(int, char **argv) { - const std::shared_ptr logger = - Logger::create_stdio("odr-back-translate", LogLevel::verbose); + try { + const std::shared_ptr logger = + Logger::create_stdio("odr-back-translate", LogLevel::verbose); - const std::string input{argv[1]}; - const std::string diff_path{argv[2]}; - const std::string output{argv[3]}; + const std::string input{argv[1]}; + const std::string diff_path{argv[2]}; + const std::string output{argv[3]}; - const DocumentFile document_file{input}; + const DocumentFile document_file{input}; - if (document_file.password_encrypted()) { - ODR_FATAL(*logger, "encrypted documents are not supported"); - return 1; - } + if (document_file.password_encrypted()) { + ODR_FATAL(*logger, "encrypted documents are not supported"); + return 1; + } - const Document document = document_file.document(); + const Document document = document_file.document(); - const std::string diff = internal::util::file::read(diff_path); - html::edit(document, diff.c_str()); + const std::string diff = internal::util::file::read(diff_path); + html::edit(document, diff.c_str()); - document.save(output); + document.save(output); - return 0; + return 0; + } catch (const std::exception &e) { + std::cerr << "error: " << e.what() << '\n'; + return 1; + } } diff --git a/cli/src/meta.cpp b/cli/src/meta.cpp index 824666040..ce527131b 100644 --- a/cli/src/meta.cpp +++ b/cli/src/meta.cpp @@ -10,34 +10,39 @@ using namespace odr; int main(const int argc, char **argv) { - const std::shared_ptr logger = - Logger::create_stdio("odr-meta", LogLevel::verbose); + try { + const std::shared_ptr logger = + Logger::create_stdio("odr-meta", LogLevel::verbose); - const std::string input{argv[1]}; + const std::string input{argv[1]}; - std::optional password; - if (argc >= 3) { - password = argv[2]; - } - - DocumentFile document_file{input}; + std::optional password; + if (argc >= 3) { + password = argv[2]; + } - if (document_file.password_encrypted() && !password) { - ODR_FATAL(*logger, "document encrypted but no password given"); - return 2; - } - if (document_file.password_encrypted()) { - try { - document_file = document_file.decrypt(*password); - } catch (const WrongPasswordError &) { - ODR_FATAL(*logger, "wrong password"); - return 1; + DocumentFile document_file{input}; + + if (document_file.password_encrypted()) { + if (!password) { + ODR_FATAL(*logger, "document encrypted but no password given"); + return 2; + } + try { + document_file = document_file.decrypt(*password); + } catch (const WrongPasswordError &) { + ODR_FATAL(*logger, "wrong password"); + return 1; + } } - } - const auto json = - internal::util::meta::meta_to_json(document_file.file_meta()); - std::cout << json.dump(4) << std::endl; + const auto json = + internal::util::meta::meta_to_json(document_file.file_meta()); + std::cout << json.dump(4) << '\n'; - return 0; + return 0; + } catch (const std::exception &e) { + std::cerr << "error: " << e.what() << '\n'; + return 1; + } } diff --git a/cli/src/server.cpp b/cli/src/server.cpp index 960fbffe8..b591d7a3e 100644 --- a/cli/src/server.cpp +++ b/cli/src/server.cpp @@ -5,83 +5,87 @@ #include #include +#include #include using namespace odr; int main(const int argc, char **argv) { - const std::shared_ptr logger = - Logger::create_stdio("odr-server", LogLevel::verbose); + try { + const std::shared_ptr logger = + Logger::create_stdio("odr-server", LogLevel::verbose); - std::string input{argv[1]}; + std::string input{argv[1]}; - std::optional password; - if (argc >= 3) { - password = argv[2]; - } + std::optional password; + if (argc >= 3) { + password = argv[2]; + } - DecodePreference decode_preference; - decode_preference.engine_priority = { - DecoderEngine::poppler, DecoderEngine::wvware, DecoderEngine::odr}; - decode_preference.as_file_type = FileType::zip; + DecodePreference decode_preference; + decode_preference.engine_priority = { + DecoderEngine::poppler, DecoderEngine::wvware, DecoderEngine::odr}; + decode_preference.as_file_type = FileType::zip; - DecodedFile decoded_file{input, decode_preference, *logger}; + DecodedFile decoded_file{input, decode_preference, *logger}; - if (decoded_file.password_encrypted() && !password) { - ODR_FATAL(*logger, "document encrypted but no password given"); - return 2; - } - if (decoded_file.password_encrypted()) { - try { - decoded_file = decoded_file.decrypt(*password); - } catch (const WrongPasswordError &) { - ODR_FATAL(*logger, "wrong password"); - return 1; + if (decoded_file.password_encrypted()) { + if (!password) { + ODR_FATAL(*logger, "document encrypted but no password given"); + return 2; + } + try { + decoded_file = decoded_file.decrypt(*password); + } catch (const WrongPasswordError &) { + ODR_FATAL(*logger, "wrong password"); + return 1; + } } - } - HttpServer::Config server_config; - HttpServer server(server_config); + HttpServer::Config server_config; + HttpServer server(server_config); - HtmlConfig html_config; - html_config.embed_images = false; - html_config.embed_shipped_resources = true; - html_config.relative_resource_paths = false; - html_config.text_document_margin = true; - html_config.editable = true; + HtmlConfig html_config; + html_config.embed_images = false; + html_config.embed_shipped_resources = true; + html_config.relative_resource_paths = false; + html_config.text_document_margin = true; + html_config.editable = true; - { - const std::string prefix = "file"; - const HtmlViews views = - server.serve_file(decoded_file, prefix, html_config); - ODR_INFO(*logger, "hosted decoded file with id: " << prefix); - for (const auto &view : views) { - ODR_INFO(*logger, - "http://localhost:8080/file/" << prefix << "/" << view.path()); + { + const std::string prefix = "file"; + const HtmlViews views = + server.serve_file(decoded_file, prefix, html_config); + ODR_INFO(*logger, "hosted decoded file with id: " << prefix); + for (const auto &view : views) { + ODR_INFO(*logger, + "http://localhost:8080/file/" << prefix << "/" << view.path()); + } } - } - if (decoded_file.is_document_file() || decoded_file.is_archive_file()) { - std::optional filesystem; - if (decoded_file.is_document_file()) { - filesystem = decoded_file.as_document_file().document().as_filesystem(); - } else if (decoded_file.is_archive_file()) { - filesystem = decoded_file.as_archive_file().archive().as_filesystem(); - } + if (decoded_file.is_document_file() || decoded_file.is_archive_file()) { + const Filesystem filesystem = + decoded_file.is_document_file() + ? decoded_file.as_document_file().document().as_filesystem() + : decoded_file.as_archive_file().archive().as_filesystem(); - const std::string prefix = "filesystem"; - const HtmlService filesystem_service = html::translate( - filesystem.value(), server_config.cache_path + "/" + prefix, - html_config, logger); - server.connect_service(filesystem_service, prefix); - ODR_INFO(*logger, "hosted filesystem with id: " << prefix); - for (const auto &view : filesystem_service.list_views()) { - ODR_INFO(*logger, - "http://localhost:8080/file/" << prefix << "/" << view.path()); + const std::string prefix = "filesystem"; + const HtmlService filesystem_service = + html::translate(filesystem, server_config.cache_path + "/" + prefix, + html_config, logger); + server.connect_service(filesystem_service, prefix); + ODR_INFO(*logger, "hosted filesystem with id: " << prefix); + for (const auto &view : filesystem_service.list_views()) { + ODR_INFO(*logger, + "http://localhost:8080/file/" << prefix << "/" << view.path()); + } } - } - server.listen("localhost", 8080); + server.listen("localhost", 8080); - return 0; + return 0; + } catch (const std::exception &e) { + std::cerr << "error: " << e.what() << '\n'; + return 1; + } } diff --git a/cli/src/translate.cpp b/cli/src/translate.cpp index 90c494947..35df73328 100644 --- a/cli/src/translate.cpp +++ b/cli/src/translate.cpp @@ -3,46 +3,52 @@ #include #include +#include #include using namespace odr; int main(const int argc, char **argv) { - const std::shared_ptr logger = - Logger::create_stdio("odr-translate", LogLevel::verbose); + try { + const std::shared_ptr logger = + Logger::create_stdio("odr-translate", LogLevel::verbose); - const std::string input{argv[1]}; - const std::string output{argv[2]}; + const std::string input{argv[1]}; + const std::string output{argv[2]}; - std::optional password; - if (argc >= 4) { - password = argv[3]; - } - - DecodedFile decoded_file{input}; + std::optional password; + if (argc >= 4) { + password = argv[3]; + } - if (decoded_file.password_encrypted() && !password) { - ODR_FATAL(*logger, "document encrypted but no password given"); - return 2; - } - if (decoded_file.password_encrypted()) { - try { - decoded_file = decoded_file.decrypt(*password); - } catch (const WrongPasswordError &) { - ODR_FATAL(*logger, "wrong password"); - return 1; + DecodedFile decoded_file{input}; + + if (decoded_file.password_encrypted()) { + if (!password) { + ODR_FATAL(*logger, "document encrypted but no password given"); + return 2; + } + try { + decoded_file = decoded_file.decrypt(*password); + } catch (const WrongPasswordError &) { + ODR_FATAL(*logger, "wrong password"); + return 1; + } } - } - HtmlConfig config; - config.editable = true; - config.format_html = true; + HtmlConfig config; + config.editable = true; + config.format_html = true; - const std::string output_tmp = output + "/tmp"; - std::filesystem::create_directories(output_tmp); - const HtmlService service = html::translate(decoded_file, output, config); - Html html = service.bring_offline(output); - std::filesystem::remove_all(output_tmp); + const std::string output_tmp = output + "/tmp"; + std::filesystem::create_directories(output_tmp); + const HtmlService service = html::translate(decoded_file, output, config); + Html html = service.bring_offline(output); + std::filesystem::remove_all(output_tmp); - return 0; + return 0; + } catch (const std::exception &e) { + std::cerr << "error: " << e.what() << '\n'; + return 1; + } } diff --git a/src/odr/document_path.cpp b/src/odr/document_path.cpp index d779681c1..f61d98d5c 100644 --- a/src/odr/document_path.cpp +++ b/src/odr/document_path.cpp @@ -99,7 +99,7 @@ bool DocumentPath::operator!=(const DocumentPath &other) const noexcept { return m_components != other.m_components; } -std::string DocumentPath::to_string() const noexcept { +std::string DocumentPath::to_string() const { std::string result; for (auto &&component : m_components) { diff --git a/src/odr/document_path.hpp b/src/odr/document_path.hpp index 6b90fe4a0..025d92d43 100644 --- a/src/odr/document_path.hpp +++ b/src/odr/document_path.hpp @@ -61,7 +61,7 @@ class DocumentPath final { bool operator==(const DocumentPath &other) const noexcept; bool operator!=(const DocumentPath &other) const noexcept; - [[nodiscard]] std::string to_string() const noexcept; + [[nodiscard]] std::string to_string() const; [[nodiscard]] bool empty() const noexcept; diff --git a/src/odr/internal/cfb/cfb_impl.cpp b/src/odr/internal/cfb/cfb_impl.cpp index 66b7bc5e8..16ff813af 100644 --- a/src/odr/internal/cfb/cfb_impl.cpp +++ b/src/odr/internal/cfb/cfb_impl.cpp @@ -217,7 +217,8 @@ Sector CompoundFileReader::resolve_next_sector(std::istream &in, const std::uint32_t fatSectorLocation = resolve_fat_sector_location(in, fatSectorNumber); const std::uint64_t address = sector_offset_to_address( - {fatSectorLocation, sector % entriesPerSector * 4}); + {fatSectorLocation, + static_cast(sector % entriesPerSector) * 4}); in.seekg(static_cast(address)); return parse_uint32(in); } @@ -225,7 +226,8 @@ Sector CompoundFileReader::resolve_next_sector(std::istream &in, Sector CompoundFileReader::resolve_next_mini_sector( std::istream &in, const std::uint32_t mini_sector) const { const SectorOffset sector_offset = normalize_sector_offset( - in, {m_header.first_mini_fat_sector_location, mini_sector * 4}); + in, {m_header.first_mini_fat_sector_location, + static_cast(mini_sector) * 4}); const std::uint64_t address = sector_offset_to_address(sector_offset); in.seekg(static_cast(address)); return parse_uint32(in); @@ -294,8 +296,8 @@ std::uint32_t CompoundFileReader::resolve_fat_sector_location( in.seekg(static_cast(address)); difatSectorLocation = parse_uint32(in); } - const std::uint64_t address = - sector_offset_to_address({difatSectorLocation, fat_sector_number * 4}); + const std::uint64_t address = sector_offset_to_address( + {difatSectorLocation, static_cast(fat_sector_number) * 4}); in.seekg(static_cast(address)); return parse_uint32(in); } diff --git a/src/odr/internal/cfb/cfb_util.hpp b/src/odr/internal/cfb/cfb_util.hpp index d5e716ad5..6e45d4246 100644 --- a/src/odr/internal/cfb/cfb_util.hpp +++ b/src/odr/internal/cfb/cfb_util.hpp @@ -10,10 +10,6 @@ #include #include -namespace odr::abstract { -class File; -} // namespace odr::abstract - namespace odr::internal::cfb::impl { class CompoundFileReader; struct CompoundFileEntry; @@ -80,8 +76,13 @@ class Archive final : public std::enable_shared_from_this { static Iterator begin(const Entry &entry) { return Iterator(entry); } static Iterator end() { return {}; } - [[nodiscard]] reference operator*() const { return *m_entry; } - [[nodiscard]] pointer operator->() const { return &*m_entry; } + // deref requires a valid (non-end) iterator + [[nodiscard]] reference operator*() const { + return *m_entry; // NOLINT(bugprone-unchecked-optional-access) + } + [[nodiscard]] pointer operator->() const { + return &*m_entry; // NOLINT(bugprone-unchecked-optional-access) + } [[nodiscard]] bool operator==(const Iterator &other) const { return m_entry == other.m_entry; diff --git a/src/odr/internal/common/path.cpp b/src/odr/internal/common/path.cpp index 7b2f929aa..bb16aeb85 100644 --- a/src/odr/internal/common/path.cpp +++ b/src/odr/internal/common/path.cpp @@ -102,7 +102,7 @@ bool Path::operator>(const Path &b) const noexcept { const std::string &Path::string() const noexcept { return m_path; } -std::filesystem::path Path::path() const noexcept { return m_path; } +std::filesystem::path Path::path() const { return m_path; } std::size_t Path::hash() const noexcept { return std::hash{}(m_path); @@ -167,7 +167,7 @@ RelPath Path::make_relative() const { return RelPath(m_path.substr(1)); } -std::string Path::basename() const noexcept { +std::string Path::basename() const { const auto find = m_path.rfind('/'); if (find == std::string::npos) { return m_path; @@ -175,7 +175,7 @@ std::string Path::basename() const noexcept { return m_path.substr(find + 1); } -std::string Path::extension() const noexcept { +std::string Path::extension() const { // The extension is the last dot-separated segment of the file name, without // the leading dot (e.g. "a.b.ppt" -> "ppt"). Delegate to std::filesystem so // edge cases (no extension, dot-files like ".bashrc") match the platform's @@ -336,7 +336,7 @@ AbsPath AbsPath::current_working_directory() { return AbsPath(std::filesystem::current_path()); } -AbsPath::AbsPath() noexcept : Path("/") {} +AbsPath::AbsPath() : Path("/") {} AbsPath::AbsPath(const char *c_string) : Path(c_string) { if (!absolute()) { @@ -382,7 +382,7 @@ AbsPath AbsPath::common_root(const AbsPath &other) const { return Path::common_root(other).as_absolute(); } -RelPath::RelPath() noexcept : Path("") {} +RelPath::RelPath() : Path("") {} RelPath::RelPath(const char *c_string) : Path(c_string) { if (!relative()) { diff --git a/src/odr/internal/common/path.hpp b/src/odr/internal/common/path.hpp index 906aafdb1..b627b572c 100644 --- a/src/odr/internal/common/path.hpp +++ b/src/odr/internal/common/path.hpp @@ -18,10 +18,10 @@ class Path { explicit Path(std::string_view string_view); explicit Path(const std::filesystem::path &path); - Path(const Path &other) noexcept = default; + Path(const Path &other) = default; Path(Path &&other) noexcept = default; - virtual ~Path() noexcept = default; - Path &operator=(const Path &other) noexcept = default; + virtual ~Path() = default; + Path &operator=(const Path &other) = default; Path &operator=(Path &&other) noexcept = default; bool operator==(const Path &other) const noexcept; @@ -30,7 +30,7 @@ class Path { bool operator>(const Path &other) const noexcept; [[nodiscard]] const std::string &string() const noexcept; - [[nodiscard]] std::filesystem::path path() const noexcept; + [[nodiscard]] std::filesystem::path path() const; [[nodiscard]] std::size_t hash() const noexcept; [[nodiscard]] bool empty() const noexcept; @@ -52,8 +52,8 @@ class Path { [[nodiscard]] AbsPath make_absolute() const; [[nodiscard]] RelPath make_relative() const; - [[nodiscard]] std::string basename() const noexcept; - [[nodiscard]] std::string extension() const noexcept; + [[nodiscard]] std::string basename() const; + [[nodiscard]] std::string extension() const; [[nodiscard]] Path parent() const; [[nodiscard]] Path join(const RelPath &other) const; @@ -111,7 +111,7 @@ class AbsPath final : public Path { public: static AbsPath current_working_directory(); - AbsPath() noexcept; + AbsPath(); explicit AbsPath(const char *c_string); explicit AbsPath(const std::string &string); explicit AbsPath(std::string_view string_view); @@ -126,7 +126,7 @@ class AbsPath final : public Path { class RelPath final : public Path { public: - RelPath() noexcept; + RelPath(); explicit RelPath(const char *c_string); explicit RelPath(const std::string &string); explicit RelPath(std::string_view string_view); diff --git a/src/odr/internal/common/table_cursor.cpp b/src/odr/internal/common/table_cursor.cpp index 87e855cf9..ec1ce8361 100644 --- a/src/odr/internal/common/table_cursor.cpp +++ b/src/odr/internal/common/table_cursor.cpp @@ -4,13 +4,13 @@ namespace odr::internal { -TableCursor::TableCursor() noexcept { m_sparse.emplace_back(); } +TableCursor::TableCursor() { m_sparse.emplace_back(); } void TableCursor::add_column(const uint32_t repeat) noexcept { m_column += repeat; } -void TableCursor::add_row(const std::uint32_t repeat) noexcept { +void TableCursor::add_row(const std::uint32_t repeat) { m_row += repeat; m_column = 0; if (repeat > 1) { @@ -27,7 +27,7 @@ void TableCursor::add_row(const std::uint32_t repeat) noexcept { void TableCursor::add_cell(const std::uint32_t colspan, const std::uint32_t rowspan, - const std::uint32_t repeat) noexcept { + const std::uint32_t repeat) { const std::uint32_t next_column = m_column + colspan * repeat; // handle rowspan diff --git a/src/odr/internal/common/table_cursor.hpp b/src/odr/internal/common/table_cursor.hpp index 9a78d6856..0ec3f8ec0 100644 --- a/src/odr/internal/common/table_cursor.hpp +++ b/src/odr/internal/common/table_cursor.hpp @@ -9,12 +9,12 @@ namespace odr::internal { class TableCursor final { public: - TableCursor() noexcept; + TableCursor(); void add_column(std::uint32_t repeat = 1) noexcept; - void add_row(std::uint32_t repeat = 1) noexcept; + void add_row(std::uint32_t repeat = 1); void add_cell(std::uint32_t colspan = 1, std::uint32_t rowspan = 1, - std::uint32_t repeat = 1) noexcept; + std::uint32_t repeat = 1); [[nodiscard]] TablePosition position() const noexcept; [[nodiscard]] std::uint32_t column() const noexcept; diff --git a/src/odr/internal/common/temporary_file.cpp b/src/odr/internal/common/temporary_file.cpp index 972dc3a91..f7e173cca 100644 --- a/src/odr/internal/common/temporary_file.cpp +++ b/src/odr/internal/common/temporary_file.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -21,7 +22,9 @@ TemporaryDiskFile::TemporaryDiskFile(const TemporaryDiskFile &) = default; TemporaryDiskFile::TemporaryDiskFile(TemporaryDiskFile &&) noexcept = default; TemporaryDiskFile::~TemporaryDiskFile() { - std::filesystem::remove(disk_path()->string()); + assert(disk_path().has_value()); + std::error_code ec; + std::filesystem::remove(disk_path()->string(), ec); } TemporaryDiskFile & diff --git a/src/odr/internal/html/document_element.cpp b/src/odr/internal/html/document_element.cpp index ae5d85d96..e977d0c14 100644 --- a/src/odr/internal/html/document_element.cpp +++ b/src/odr/internal/html/document_element.cpp @@ -401,9 +401,9 @@ void html::translate_image(const Element &element, const WritingState &state) { odr::HtmlResource resource; HtmlResourceLocation resource_location; if (image.is_internal()) { - resource = HtmlResource::create(HtmlResourceType::image, "image/jpg", - image.href(), image.href(), - image.file().value(), false, false, true); + resource = + HtmlResource::create(HtmlResourceType::image, "image/jpg", image.href(), + image.href(), image.file(), false, false, true); resource_location = state.config().resource_locator(resource, state.config()); } else { @@ -424,6 +424,8 @@ void html::translate_image(const Element &element, const WritingState &state) { clb("src", resource_location.value()); } else { clb("src", [&](std::ostream &o) { + // reached only for internal images, which have a file + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) translate_image_src(image.file().value(), o, state.config()); }); } diff --git a/src/odr/internal/html/html_service.cpp b/src/odr/internal/html/html_service.cpp index d710261d0..ca36f2db1 100644 --- a/src/odr/internal/html/html_service.cpp +++ b/src/odr/internal/html/html_service.cpp @@ -67,11 +67,12 @@ bool HtmlResource::is_external() const { return m_is_external; } bool HtmlResource::is_accessible() const { return m_is_accessible; } void HtmlResource::write_resource(std::ostream &os) const { - if (!is_accessible() || !file().has_value()) { + const std::optional &resource_file = file(); + if (!is_accessible() || !resource_file.has_value()) { throw ResourceNotAccessible(name(), path()); } - util::stream::pipe(*file()->stream(), os); + util::stream::pipe(*resource_file->stream(), os); } } // namespace odr::internal::html diff --git a/src/odr/internal/html/pdf_file.cpp b/src/odr/internal/html/pdf_file.cpp index 223d0e2d3..b7a18e342 100644 --- a/src/odr/internal/html/pdf_file.cpp +++ b/src/odr/internal/html/pdf_file.cpp @@ -134,7 +134,7 @@ class HtmlServiceImpl final : public HtmlService { std::string unicode = font->to_unicode(glyphs); if (unicode.find("Colored Line") != std::string::npos) { - std::cout << "hi" << std::endl; + std::cout << "hi" << '\n'; } out.write_element_begin( @@ -152,24 +152,23 @@ class HtmlServiceImpl final : public HtmlService { pdf::Font *font = page->resources->font.at(font_ref); double size = state.current().text.size; - std::cout << font->object << std::endl; + std::cout << font->object << '\n'; for (const auto &element : op.arguments[0].as_array()) { if (element.is_real()) { - std::cout << "spacing: " << element.as_real() << std::endl; + std::cout << "spacing: " << element.as_real() << '\n'; } else if (element.is_string()) { const std::string &glyphs = element.as_string(); std::string unicode = font->to_unicode(glyphs); std::cout << "show text manual spacing: font=" << font - << ", size=" << size << ", text=" << unicode - << std::endl; + << ", size=" << size << ", text=" << unicode << '\n'; } } } else if (op.type == pdf::GraphicsOperatorType::show_text_next_line) { - std::cout << "TODO show_text_next_line" << std::endl; + std::cout << "TODO show_text_next_line" << '\n'; } else if (op.type == pdf::GraphicsOperatorType::show_text_next_line_set_spacing) { - std::cout << "TODO show_text_next_line_set_spacing" << std::endl; + std::cout << "TODO show_text_next_line_set_spacing" << '\n'; } } diff --git a/src/odr/internal/html/wvware_wrapper.cpp b/src/odr/internal/html/wvware_wrapper.cpp index 619685d9e..d0d24921b 100644 --- a/src/odr/internal/html/wvware_wrapper.cpp +++ b/src/odr/internal/html/wvware_wrapper.cpp @@ -245,6 +245,8 @@ void output_from_unicode(const wvParseStruct *ps, const std::uint16_t eachchar, { g_iconv_handle = g_iconv_open(outputtype, "UCS-2"); + // (GIConv)-1 is glib's documented error value + // NOLINTNEXTLINE(performance-no-int-to-ptr) if (g_iconv_handle == reinterpret_cast(-1)) { std::cerr << "g_iconv_open fail: " << errno << ", cannot convert UCS-2 to " << outputtype << "\n"; @@ -408,11 +410,11 @@ int dump_metafile(wvParseStruct *ps, const std::string &name, fputc(read_8ubit(pwv), tmp); } - rewind(tmp); + fseek(tmp, 0, SEEK_SET); decompress(tmp, out, bitmap->m_cbSave, bitmap->m_cb); fclose(tmp); - rewind(out); + fseek(out, 0, SEEK_SET); for (std::size_t i = 0; i < bitmap->m_cb; i++) { fputc(fgetc(out), fd); diff --git a/src/odr/internal/odf/odf_document.cpp b/src/odr/internal/odf/odf_document.cpp index c5b505bcb..6f1c2d511 100644 --- a/src/odr/internal/odf/odf_document.cpp +++ b/src/odr/internal/odf/odf_document.cpp @@ -876,7 +876,7 @@ class ElementAdapter final : public abstract::ElementAdapter, try { const AbsPath path = Path(image_href(element_id)).make_absolute(); return m_document->as_filesystem()->is_file(path); - } catch (...) { + } catch (...) { // NOLINT(bugprone-empty-catch): any error => not internal } return false; } diff --git a/src/odr/internal/odf/odf_file.cpp b/src/odr/internal/odf/odf_file.cpp index 1701a7007..01c2cd2cd 100644 --- a/src/odr/internal/odf/odf_file.cpp +++ b/src/odr/internal/odf/odf_file.cpp @@ -51,10 +51,14 @@ std::string_view OpenDocumentFile::mimetype() const noexcept { FileMeta OpenDocumentFile::file_meta() const noexcept { return m_file_meta; } DocumentType OpenDocumentFile::document_type() const { + // document_meta is always set for document files + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) return m_file_meta.document_meta.value().document_type; } DocumentMeta OpenDocumentFile::document_meta() const { + // document_meta is always set for document files + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) return m_file_meta.document_meta.value(); } diff --git a/src/odr/internal/odf/odf_parser.cpp b/src/odr/internal/odf/odf_parser.cpp index 56e5b2db6..39e422b74 100644 --- a/src/odr/internal/odf/odf_parser.cpp +++ b/src/odr/internal/odf/odf_parser.cpp @@ -80,7 +80,8 @@ parse_text_element(ElementRegistry ®istry, const pugi::xml_node first) { for (; is_text_node(last.next_sibling()); last = last.next_sibling()) { } - const auto &[element_id, _, __] = registry.create_text_element(first, last); + const auto &[element_id, unused1, unused2] = + registry.create_text_element(first, last); return {element_id, last.next_sibling()}; } @@ -239,8 +240,9 @@ parse_sheet(ElementRegistry ®istry, const pugi::xml_node node) { for (std::uint32_t column_repeat = 0; column_repeat < columns_repeated; ++column_repeat) { - const auto &[cell_id, _, __] = registry.create_sheet_cell_element( - cell_node, cursor.position(), is_repeated); + const auto &[cell_id, unused1, unused2] = + registry.create_sheet_cell_element(cell_node, cursor.position(), + is_repeated); registry.append_sheet_cell(element_id, cell_id); sheet.register_cell(cursor.column(), cursor.row(), 1, 1, cell_node, cell_id); diff --git a/src/odr/internal/odf/odf_style.cpp b/src/odr/internal/odf/odf_style.cpp index 4b28bcd77..fe54381df 100644 --- a/src/odr/internal/odf/odf_style.cpp +++ b/src/odr/internal/odf/odf_style.cpp @@ -22,7 +22,8 @@ std::optional read_measure(const pugi::xml_attribute attribute) { if (attribute) { try { return Measure(attribute.value()); - } catch (...) { + } catch (...) { // NOLINT(bugprone-empty-catch) + // an unparsable measure yields nullopt // TODO log } } diff --git a/src/odr/internal/oldms/oldms_file.cpp b/src/odr/internal/oldms/oldms_file.cpp index 0c169ba70..feac89622 100644 --- a/src/odr/internal/oldms/oldms_file.cpp +++ b/src/odr/internal/oldms/oldms_file.cpp @@ -83,10 +83,14 @@ std::string_view LegacyMicrosoftFile::mimetype() const noexcept { FileMeta LegacyMicrosoftFile::file_meta() const noexcept { return m_file_meta; } DocumentType LegacyMicrosoftFile::document_type() const { + // document_meta is always set for document files + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) return m_file_meta.document_meta.value().document_type; } DocumentMeta LegacyMicrosoftFile::document_meta() const { + // document_meta is always set for document files + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) return m_file_meta.document_meta.value(); } diff --git a/src/odr/internal/oldms/text/doc_io.cpp b/src/odr/internal/oldms/text/doc_io.cpp index cb890a52d..b2b08a6e5 100644 --- a/src/odr/internal/oldms/text/doc_io.cpp +++ b/src/odr/internal/oldms/text/doc_io.cpp @@ -90,13 +90,13 @@ std::size_t text::determine_size_Fib(std::istream &in) { ignore(sizeof(FibBase)); const std::uint16_t csw = read_uint16_t(); - ignore(csw * 2); + ignore(static_cast(csw) * 2); const std::uint16_t cslw = read_uint16_t(); - ignore(cslw * 4); + ignore(static_cast(cslw) * 4); const std::uint16_t cbRgFcLcb = read_uint16_t(); - ignore(cbRgFcLcb * 8); + ignore(static_cast(cbRgFcLcb) * 8); const std::uint16_t cswNew = read_uint16_t(); - ignore(cswNew * 2); + ignore(static_cast(cswNew) * 2); return result; } @@ -105,20 +105,22 @@ void text::read(std::istream &in, ParsedFib &out) { read(in, out.base); util::byte_stream::read(in, out.csw); - if (out.csw * 2 < sizeof(out.fibRgW)) { + if (static_cast(out.csw) * 2 < sizeof(out.fibRgW)) { throw std::runtime_error("Unexpected Fib.csw value: " + std::to_string(out.csw)); } util::byte_stream::read(in, out.fibRgW); - in.ignore(static_cast(out.csw * 2 - sizeof(out.fibRgW))); + in.ignore(static_cast(static_cast(out.csw) * 2 - + sizeof(out.fibRgW))); util::byte_stream::read(in, out.cslw); - if (out.cslw * 4 < sizeof(out.fibRgLw)) { + if (static_cast(out.cslw) * 4 < sizeof(out.fibRgLw)) { throw std::runtime_error("Unexpected Fib.cslw value: " + std::to_string(out.cslw)); } util::byte_stream::read(in, out.fibRgLw); - in.ignore(static_cast(out.cslw * 4 - sizeof(out.fibRgLw))); + in.ignore(static_cast( + static_cast(out.cslw) * 4 - sizeof(out.fibRgLw))); // ccpText ([MS-DOC] 2.5.5) MUST be >= 0; reject a negative value as // malformed. @@ -128,8 +130,9 @@ void text::read(std::istream &in, ParsedFib &out) { } util::byte_stream::read(in, out.cbRgFcLcb); - const auto fibRgFcLcb = std::make_unique(out.cbRgFcLcb * 8); - in.read(fibRgFcLcb.get(), out.cbRgFcLcb * 8); + const auto fibRgFcLcb = + std::make_unique(static_cast(out.cbRgFcLcb) * 8); + in.read(fibRgFcLcb.get(), static_cast(out.cbRgFcLcb) * 8); util::byte_stream::read(in, out.cswNew); if (out.cswNew > 0) { @@ -137,7 +140,7 @@ void text::read(std::istream &in, ParsedFib &out) { read(in, *out.fibRgCswNew); } const std::uint16_t nFib = - out.cswNew != 0 ? out.fibRgCswNew.value().nFibNew : out.base.nFib; + out.fibRgCswNew.has_value() ? out.fibRgCswNew->nFibNew : out.base.nFib; out.fibRgFcLcb = type_dispatch_FibRgFcLcb( nFib, [&](const T) -> std::unique_ptr { @@ -147,7 +150,8 @@ void text::read(std::istream &in, ParsedFib &out) { // shorter one leaves the rest zero-initialised. fcClx is always // covered. const std::size_t copy = - std::min(sizeof(FibRgFcLcbType), out.cbRgFcLcb * 8); + std::min(sizeof(FibRgFcLcbType), + static_cast(out.cbRgFcLcb) * 8); std::memcpy(result.get(), fibRgFcLcb.get(), copy); return result; }); diff --git a/src/odr/internal/oldms_wvware/wvware_oldms_file.cpp b/src/odr/internal/oldms_wvware/wvware_oldms_file.cpp index 255d57d27..2ff7a810e 100644 --- a/src/odr/internal/oldms_wvware/wvware_oldms_file.cpp +++ b/src/odr/internal/oldms_wvware/wvware_oldms_file.cpp @@ -26,9 +26,10 @@ WvWareLegacyMicrosoftFile::WvWareLegacyMicrosoftFile( m_parser_state = std::make_shared(); - if (m_file->disk_path().has_value()) { + if (const std::optional disk_path = m_file->disk_path(); + disk_path.has_value()) { m_parser_state->gsf_input = - gsf_input_stdio_new(m_file->disk_path()->string().c_str(), &error); + gsf_input_stdio_new(disk_path->string().c_str(), &error); } else if (m_file->memory_data() != nullptr) { m_parser_state->gsf_input = gsf_input_memory_new( reinterpret_cast(m_file->memory_data()), @@ -98,6 +99,8 @@ DocumentType WvWareLegacyMicrosoftFile::document_type() const { } DocumentMeta WvWareLegacyMicrosoftFile::document_meta() const { + // document_meta is always set for document files + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) return m_file_meta.document_meta.value(); } diff --git a/src/odr/internal/oldms_wvware/wvware_oldms_file.hpp b/src/odr/internal/oldms_wvware/wvware_oldms_file.hpp index b090bedcf..aab3bed2f 100644 --- a/src/odr/internal/oldms_wvware/wvware_oldms_file.hpp +++ b/src/odr/internal/oldms_wvware/wvware_oldms_file.hpp @@ -6,8 +6,10 @@ #include +// NOLINTNEXTLINE(bugprone-reserved-identifier) - wvware's own (C) struct name struct _wvParseStruct; -using wvParseStruct = struct _wvParseStruct; +using wvParseStruct = + struct _wvParseStruct; // NOLINT(bugprone-reserved-identifier) namespace odr::internal { diff --git a/src/odr/internal/ooxml/ooxml_crypto.cpp b/src/odr/internal/ooxml/ooxml_crypto.cpp index 243d3e3bc..5a69985a9 100644 --- a/src/odr/internal/ooxml/ooxml_crypto.cpp +++ b/src/odr/internal/ooxml/ooxml_crypto.cpp @@ -31,7 +31,9 @@ ECMA376Standard::ECMA376Standard(const std::string &encryption_info) { std::memcpy(&m_encryption_header, offset, sizeof(m_encryption_header)); const std::u16string csp_name_u16( reinterpret_cast(offset + sizeof(m_encryption_header))); - const std::string csp_name = util::string::u16string_to_string(csp_name_u16); + // the CSP name field is parsed but currently unused + [[maybe_unused]] const std::string csp_name = + util::string::u16string_to_string(csp_name_u16); offset += standard_header.encryption_header_size; std::memcpy(&m_encryption_verifier, offset, sizeof(m_encryption_verifier)); @@ -41,8 +43,7 @@ ECMA376Standard::ECMA376Standard(const std::string &encryption_info) { offset, encryption_info.size() - (offset - encryption_info.data())); } -std::string -ECMA376Standard::derive_key(const std::string &password) const noexcept { +std::string ECMA376Standard::derive_key(const std::string &password) const { // https://msdn.microsoft.com/en-us/library/dd925430(v=office.12).aspx std::string hash; @@ -60,7 +61,9 @@ ECMA376Standard::derive_key(const std::string &password) const noexcept { std::string ibytes(4, ' '); for (std::uint32_t i = 0; i < ITER_COUNT; ++i) { util::byte::to_little_endian(i, ibytes); - hash = internal::crypto::util::sha1(ibytes + hash); + std::string ih = ibytes; + ih += hash; + hash = internal::crypto::util::sha1(ih); } util::byte::to_little_endian(static_cast(0), ibytes); hash = internal::crypto::util::sha1(hash + ibytes); @@ -87,7 +90,7 @@ ECMA376Standard::derive_key(const std::string &password) const noexcept { return result; } -bool ECMA376Standard::verify(const std::string &key) const noexcept { +bool ECMA376Standard::verify(const std::string &key) const { // https://msdn.microsoft.com/en-us/library/dd926426(v=office.12).aspx const std::string verifier = internal::crypto::util::decrypt_aes_ecb( @@ -102,7 +105,7 @@ bool ECMA376Standard::verify(const std::string &key) const noexcept { } std::string ECMA376Standard::decrypt(const std::string &encrypted_package, - const std::string &key) const noexcept { + const std::string &key) const { const std::uint64_t total_size = *reinterpret_cast(encrypted_package.data()); std::string result = @@ -127,28 +130,23 @@ Util::Util(const std::string &encryption_info) { version_info.major == 4) && version_info.minor == 2) { impl = std::make_unique(encryption_info); - } else if (version_info.major == 4 && version_info.minor == 4) { - throw MsUnsupportedCryptoAlgorithm(); // agile - } else if ((version_info.major == 3 || version_info.major == 4) && - version_info.minor == 3) { - throw MsUnsupportedCryptoAlgorithm(); // extensible } else { - throw MsUnsupportedCryptoAlgorithm(); // unknown + // everything else (agile 4.4, extensible 3.3/4.3, and unknown variants) is + // unsupported + throw MsUnsupportedCryptoAlgorithm(); } } -Util::~Util() noexcept = default; +Util::~Util() = default; -std::string Util::derive_key(const std::string &password) const noexcept { +std::string Util::derive_key(const std::string &password) const { return impl->derive_key(password); } -bool Util::verify(const std::string &key) const noexcept { - return impl->verify(key); -} +bool Util::verify(const std::string &key) const { return impl->verify(key); } std::string Util::decrypt(const std::string &encrypted_package, - const std::string &key) const noexcept { + const std::string &key) const { return impl->decrypt(encrypted_package, key); } diff --git a/src/odr/internal/ooxml/ooxml_crypto.hpp b/src/odr/internal/ooxml/ooxml_crypto.hpp index 9e6d3c75b..e677a10fc 100644 --- a/src/odr/internal/ooxml/ooxml_crypto.hpp +++ b/src/odr/internal/ooxml/ooxml_crypto.hpp @@ -46,13 +46,13 @@ struct StandardHeader { class Algorithm { public: - virtual ~Algorithm() noexcept = default; + virtual ~Algorithm() = default; [[nodiscard]] virtual std::string - derive_key(const std::string &password) const noexcept = 0; - [[nodiscard]] virtual bool verify(const std::string &key) const noexcept = 0; + derive_key(const std::string &password) const = 0; + [[nodiscard]] virtual bool verify(const std::string &key) const = 0; [[nodiscard]] virtual std::string decrypt(const std::string &encrypted_package, - const std::string &key) const noexcept = 0; + const std::string &key) const = 0; }; class ECMA376Standard final : public Algorithm { @@ -62,11 +62,10 @@ class ECMA376Standard final : public Algorithm { explicit ECMA376Standard(const std::string &encryption_info); [[nodiscard]] std::string - derive_key(const std::string &password) const noexcept override; - [[nodiscard]] bool verify(const std::string &key) const noexcept override; - [[nodiscard]] std::string - decrypt(const std::string &encrypted_package, - const std::string &key) const noexcept override; + derive_key(const std::string &password) const override; + [[nodiscard]] bool verify(const std::string &key) const override; + [[nodiscard]] std::string decrypt(const std::string &encrypted_package, + const std::string &key) const override; private: static constexpr auto ITER_COUNT = 50000; @@ -79,14 +78,13 @@ class ECMA376Standard final : public Algorithm { class Util final : public Algorithm { public: explicit Util(const std::string &encryption_info); - ~Util() noexcept override; + ~Util() override; [[nodiscard]] std::string - derive_key(const std::string &password) const noexcept override; - [[nodiscard]] bool verify(const std::string &key) const noexcept override; - [[nodiscard]] std::string - decrypt(const std::string &encrypted_package, - const std::string &key) const noexcept override; + derive_key(const std::string &password) const override; + [[nodiscard]] bool verify(const std::string &key) const override; + [[nodiscard]] std::string decrypt(const std::string &encrypted_package, + const std::string &key) const override; private: std::unique_ptr impl; diff --git a/src/odr/internal/ooxml/ooxml_file.cpp b/src/odr/internal/ooxml/ooxml_file.cpp index f39543e6a..923e63a49 100644 --- a/src/odr/internal/ooxml/ooxml_file.cpp +++ b/src/odr/internal/ooxml/ooxml_file.cpp @@ -44,10 +44,14 @@ std::string_view OfficeOpenXmlFile::mimetype() const noexcept { FileMeta OfficeOpenXmlFile::file_meta() const noexcept { return m_file_meta; } DocumentType OfficeOpenXmlFile::document_type() const { + // document_meta is always set for document files + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) return m_file_meta.document_meta.value().document_type; } DocumentMeta OfficeOpenXmlFile::document_meta() const { + // document_meta is always set for document files + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) return m_file_meta.document_meta.value(); } diff --git a/src/odr/internal/ooxml/ooxml_util.cpp b/src/odr/internal/ooxml/ooxml_util.cpp index f8c8af7d7..c9b6d73f4 100644 --- a/src/odr/internal/ooxml/ooxml_util.cpp +++ b/src/odr/internal/ooxml/ooxml_util.cpp @@ -252,11 +252,13 @@ std::optional ooxml::read_border_node(const pugi::xml_node node) { if (std::strcmp("nil", val) == 0) { return {}; } + const std::optional size = + read_half_point_attribute(node.attribute("w:sz")); + if (!size.has_value()) { + return {}; + } std::string result; - result - .append( - read_half_point_attribute(node.attribute("w:sz")).value().to_string()) - .append(" "); + result.append(size->to_string()).append(" "); if (std::strcmp("none", val) == 0) { result.append(node.attribute("w:val").value()).append(" "); } else { diff --git a/src/odr/internal/ooxml/presentation/ooxml_presentation_document.cpp b/src/odr/internal/ooxml/presentation/ooxml_presentation_document.cpp index ceb1de3a9..ebc76ac49 100644 --- a/src/odr/internal/ooxml/presentation/ooxml_presentation_document.cpp +++ b/src/odr/internal/ooxml/presentation/ooxml_presentation_document.cpp @@ -516,7 +516,7 @@ class ElementAdapter final : public abstract::ElementAdapter, try { const AbsPath path = Path(image_href(element_id)).make_absolute(); return m_document->as_filesystem()->is_file(path); - } catch (...) { + } catch (...) { // NOLINT(bugprone-empty-catch): any error => not internal } return false; } diff --git a/src/odr/internal/ooxml/presentation/ooxml_presentation_parser.cpp b/src/odr/internal/ooxml/presentation/ooxml_presentation_parser.cpp index 7ba89ccf8..71364f8e4 100644 --- a/src/odr/internal/ooxml/presentation/ooxml_presentation_parser.cpp +++ b/src/odr/internal/ooxml/presentation/ooxml_presentation_parser.cpp @@ -82,7 +82,8 @@ parse_text_element(ElementRegistry ®istry, last = last.next_sibling()) { } - const auto &[element_id, _, __] = registry.create_text_element(first, last); + const auto &[element_id, unused1, unused2] = + registry.create_text_element(first, last); return {element_id, last.next_sibling()}; } diff --git a/src/odr/internal/ooxml/spreadsheet/ooxml_spreadsheet_document.cpp b/src/odr/internal/ooxml/spreadsheet/ooxml_spreadsheet_document.cpp index a67503b87..5afa6d8e3 100644 --- a/src/odr/internal/ooxml/spreadsheet/ooxml_spreadsheet_document.cpp +++ b/src/odr/internal/ooxml/spreadsheet/ooxml_spreadsheet_document.cpp @@ -413,7 +413,7 @@ class ElementAdapter final : public abstract::ElementAdapter, try { const AbsPath path = Path(image_href(element_id)).make_absolute(); return m_document->as_filesystem()->is_file(path); - } catch (...) { + } catch (...) { // NOLINT(bugprone-empty-catch): any error => not internal } return false; } diff --git a/src/odr/internal/ooxml/spreadsheet/ooxml_spreadsheet_parser.cpp b/src/odr/internal/ooxml/spreadsheet/ooxml_spreadsheet_parser.cpp index 0e4686b5d..b43ace04d 100644 --- a/src/odr/internal/ooxml/spreadsheet/ooxml_spreadsheet_parser.cpp +++ b/src/odr/internal/ooxml/spreadsheet/ooxml_spreadsheet_parser.cpp @@ -115,7 +115,7 @@ parse_sheet_element(ElementRegistry ®istry, const ParseContext &context, for (const pugi::xml_node cell_node : row_node.children("c")) { TablePosition position(cell_node.attribute("r").value()); - const auto &[cell_id, _, __] = + const auto &[cell_id, unused1, unused2] = registry.create_sheet_cell_element(cell_node, position); registry.append_sheet_cell(element_id, cell_id); sheet.register_cell(position.column, position.row, cell_node, cell_id); @@ -191,7 +191,8 @@ parse_text_element(ElementRegistry ®istry, last = last.next_sibling()) { } - const auto &[element_id, _, __] = registry.create_text_element(first, last); + const auto &[element_id, unused1, unused2] = + registry.create_text_element(first, last); return {element_id, last.next_sibling()}; } diff --git a/src/odr/internal/ooxml/text/ooxml_text_document.cpp b/src/odr/internal/ooxml/text/ooxml_text_document.cpp index 462f32790..b6058acf4 100644 --- a/src/odr/internal/ooxml/text/ooxml_text_document.cpp +++ b/src/odr/internal/ooxml/text/ooxml_text_document.cpp @@ -495,7 +495,7 @@ class ElementAdapter final : public abstract::ElementAdapter, try { const AbsPath path = Path(image_href(element_id)).make_absolute(); return m_document->as_filesystem()->is_file(path); - } catch (...) { + } catch (...) { // NOLINT(bugprone-empty-catch): any error => not internal } return false; } diff --git a/src/odr/internal/ooxml/text/ooxml_text_parser.cpp b/src/odr/internal/ooxml/text/ooxml_text_parser.cpp index a5da87d57..58c1d4c85 100644 --- a/src/odr/internal/ooxml/text/ooxml_text_parser.cpp +++ b/src/odr/internal/ooxml/text/ooxml_text_parser.cpp @@ -79,7 +79,8 @@ parse_text_element(ElementRegistry ®istry, const pugi::xml_node first) { last = last.next_sibling()) { } - const auto &[element_id, _, __] = registry.create_text_element(first, last); + const auto &[element_id, unused1, unused2] = + registry.create_text_element(first, last); return {element_id, last.next_sibling()}; } @@ -131,8 +132,8 @@ parse_list_element(ElementRegistry ®istry, pugi::xml_node node) { registry.append_child(base_id, item_id); - auto [child_id, __] = parse_element_tree(registry, ElementType::paragraph, - node, parse_any_element_children); + auto [child_id, unused] = parse_element_tree( + registry, ElementType::paragraph, node, parse_any_element_children); registry.append_child(item_id, child_id); } @@ -164,7 +165,8 @@ parse_table_element(ElementRegistry ®istry, const pugi::xml_node node) { return {null_element_id, pugi::xml_node()}; } - const auto &[element_id, _, __] = registry.create_table_element(node); + const auto &[element_id, unused1, unused2] = + registry.create_table_element(node); for (const pugi::xml_node column_node : node.child("w:tblGrid").children("w:gridCol")) { diff --git a/src/odr/internal/open_strategy.hpp b/src/odr/internal/open_strategy.hpp index 24ef93ef8..fa8427749 100644 --- a/src/odr/internal/open_strategy.hpp +++ b/src/odr/internal/open_strategy.hpp @@ -16,10 +16,6 @@ class DecodedFile; class DocumentFile; } // namespace odr::internal::abstract -namespace odr::internal::common { -class Path; -} // namespace odr::internal::common - namespace odr::internal::open_strategy { std::vector diff --git a/src/odr/internal/pdf/pdf_document_parser.cpp b/src/odr/internal/pdf/pdf_document_parser.cpp index 89202107a..efa85d66d 100644 --- a/src/odr/internal/pdf/pdf_document_parser.cpp +++ b/src/odr/internal/pdf/pdf_document_parser.cpp @@ -592,6 +592,8 @@ std::string DocumentParser::read_object_stream(const IndirectObject &object) { throw std::runtime_error("unknown length property"); } + // a stream object always carries a stream position + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) in().seekg(object.stream_position.value()); std::string raw = m_parser.read_stream(static_cast(size)); @@ -734,6 +736,8 @@ std::pair DocumentParser::read_trailer_chain() { } } + // a valid xref chain always sets the trailer + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) return {std::move(result_xref), std::move(result_trailer).value()}; } @@ -867,7 +871,7 @@ void DocumentParser::recover_xref() { const auto &[key, value] : dict) { trailer[key] = value; // last trailer wins per key } - } catch (const std::exception &) { + } catch (const std::exception &) { // NOLINT(bugprone-empty-catch) // ignore a malformed trailer and keep scanning } stream.clear(); @@ -917,7 +921,7 @@ void DocumentParser::index_object_streams() { static_cast(reference.id), static_cast(i)})); } - } catch (const std::exception &) { + } catch (const std::exception &) { // NOLINT(bugprone-empty-catch) // an unreadable (or, when encrypted, undecryptable) object stream is // simply not indexed } @@ -941,7 +945,7 @@ void DocumentParser::recover_root() { m_trailer["Root"] = Object(reference); return; } - } catch (const std::exception &) { + } catch (const std::exception &) { // NOLINT(bugprone-empty-catch) // skip objects that fail to read during the catalog search } } @@ -950,6 +954,10 @@ void DocumentParser::recover_root() { void DocumentParser::decrypt_strings(Object &object, const ObjectReference &reference) { + // only ever called once a decryptor is installed; guard keeps that explicit + if (!m_decryptor.has_value()) { + return; + } if (object.is_standard_string()) { object = Object(StandardString( m_decryptor->decrypt_string(reference, object.as_standard_string()))); diff --git a/src/odr/internal/pdf/pdf_encryption.cpp b/src/odr/internal/pdf/pdf_encryption.cpp index b12ff0bf8..4825125fe 100644 --- a/src/odr/internal/pdf/pdf_encryption.cpp +++ b/src/odr/internal/pdf/pdf_encryption.cpp @@ -105,7 +105,11 @@ std::string hash_r6(const std::string &password, const std::string &salt, for (int round = 0; round < 64 || static_cast(e.back()) > round - 32; ++round) { - const std::string block = password + k + udata; + std::string block; + block.reserve(password.size() + k.size() + udata.size()); + block += password; + block += k; + block += udata; std::string k1; k1.reserve(block.size() * 64); for (int i = 0; i < 64; ++i) { diff --git a/src/odr/internal/pdf/pdf_file_parser.cpp b/src/odr/internal/pdf/pdf_file_parser.cpp index 4696b7308..9cc1ee9f2 100644 --- a/src/odr/internal/pdf/pdf_file_parser.cpp +++ b/src/odr/internal/pdf/pdf_file_parser.cpp @@ -145,7 +145,8 @@ std::string FileParser::read_stream(const std::int32_t size) { void FileParser::read_header() { const std::string header1 = m_parser.read_line(); - const std::string header2 = m_parser.read_line(); + // the second line is an optional binary-marker comment; read past it + [[maybe_unused]] const std::string header2 = m_parser.read_line(); if (!util::string::starts_with(header1, "%PDF-")) { throw std::runtime_error("illegal header"); diff --git a/src/odr/internal/pdf/pdf_graphics_operator_parser.cpp b/src/odr/internal/pdf/pdf_graphics_operator_parser.cpp index cb9e3d0bf..c43d519f0 100644 --- a/src/odr/internal/pdf/pdf_graphics_operator_parser.cpp +++ b/src/odr/internal/pdf/pdf_graphics_operator_parser.cpp @@ -163,7 +163,7 @@ GraphicsOperator GraphicsOperatorParser::read_operator() { const std::string operator_name = read_operator_name(); result.type = operator_name_to_type(operator_name); if (result.type == GraphicsOperatorType::unknown) { - std::cerr << "unknown operator: " << operator_name << std::endl; + std::cerr << "unknown operator: " << operator_name << '\n'; } // After `ID` the raw image bytes follow inline; consume them up to `EI` so diff --git a/src/odr/internal/pdf/pdf_graphics_state.cpp b/src/odr/internal/pdf/pdf_graphics_state.cpp index c7a790d91..5c4c75ae8 100644 --- a/src/odr/internal/pdf/pdf_graphics_state.cpp +++ b/src/odr/internal/pdf/pdf_graphics_state.cpp @@ -60,7 +60,7 @@ void GraphicsState::execute(const GraphicsOperator &op) { current().general.miter_limit = op.arguments.at(0).as_real(); break; case GraphicsOperatorType::set_dash_pattern: - std::cout << "dash pattern not implemented" << std::endl; + std::cout << "dash pattern not implemented" << '\n'; break; case GraphicsOperatorType::set_color_rendering_intent: current().general.color_rendering_intent = op.arguments.at(0).as_name(); @@ -146,11 +146,11 @@ void GraphicsState::execute(const GraphicsOperator &op) { break; case GraphicsOperatorType::set_stroke_color: // TODO - std::cout << "stroke color not implemented" << std::endl; + std::cout << "stroke color not implemented" << '\n'; break; case GraphicsOperatorType::set_stroke_color_name: // TODO - std::cout << "stroke color name not implemented" << std::endl; + std::cout << "stroke color name not implemented" << '\n'; break; case GraphicsOperatorType::set_stroke_grey_color: current().stroke_color.grey = op.arguments.at(0).as_real(); @@ -172,11 +172,11 @@ void GraphicsState::execute(const GraphicsOperator &op) { break; case GraphicsOperatorType::set_other_color: // TODO - std::cout << "other color not implemented" << std::endl; + std::cout << "other color not implemented" << '\n'; break; case GraphicsOperatorType::set_other_color_name: // TODO - std::cout << "other color name not implemented" << std::endl; + std::cout << "other color name not implemented" << '\n'; break; case GraphicsOperatorType::set_other_grey_color: current().other_color.grey = op.arguments.at(0).as_real(); diff --git a/src/odr/internal/pdf_poppler/poppler_pdf_file.cpp b/src/odr/internal/pdf_poppler/poppler_pdf_file.cpp index a2495ba4e..e5a4c7a61 100644 --- a/src/odr/internal/pdf_poppler/poppler_pdf_file.cpp +++ b/src/odr/internal/pdf_poppler/poppler_pdf_file.cpp @@ -22,6 +22,7 @@ void PopplerPdfFile::open(const std::optional &password) { if (const std::shared_ptr disk_file = std::dynamic_pointer_cast(m_file); disk_file != nullptr) { + // NOLINTNEXTLINE(bugprone-unchecked-optional-access): a DiskFile has a path auto file_path_goo = std::make_unique(disk_file->disk_path()->string().c_str()); m_pdf_doc = std::make_shared(std::move(file_path_goo), password_goo, diff --git a/src/odr/internal/svm/svm_format.cpp b/src/odr/internal/svm/svm_format.cpp index 2d825793b..f9bdc8856 100644 --- a/src/odr/internal/svm/svm_format.cpp +++ b/src/odr/internal/svm/svm_format.cpp @@ -18,7 +18,8 @@ std::string svm::read_ascii_string(std::istream &in, std::string svm::read_utf16_string(std::istream &in, const std::uint32_t length) { std::u16string result_u16(length, ' '); - in.read(reinterpret_cast(result_u16.data()), length * 2); + in.read(reinterpret_cast(result_u16.data()), + static_cast(length) * 2); return util::string::u16string_to_string(result_u16); } diff --git a/src/odr/internal/zip/zip_archive.hpp b/src/odr/internal/zip/zip_archive.hpp index 2d5b7325d..6fee803b6 100644 --- a/src/odr/internal/zip/zip_archive.hpp +++ b/src/odr/internal/zip/zip_archive.hpp @@ -11,11 +11,6 @@ namespace odr::internal::abstract { class File; } -namespace odr::internal::common { -class MemoryFile; -class DiskFile; -} // namespace odr::internal::common - namespace odr::internal::zip { namespace util { class Archive; diff --git a/src/odr/internal/zip/zip_util.hpp b/src/odr/internal/zip/zip_util.hpp index 866cd5663..09f00ad4f 100644 --- a/src/odr/internal/zip/zip_util.hpp +++ b/src/odr/internal/zip/zip_util.hpp @@ -13,8 +13,6 @@ namespace odr::internal { class RelPath; -class MemoryFile; -class DiskFile; } // namespace odr::internal namespace odr::internal::zip::util { diff --git a/src/odr/logger.hpp b/src/odr/logger.hpp index c7de92c9b..2c2bfeb67 100644 --- a/src/odr/logger.hpp +++ b/src/odr/logger.hpp @@ -66,6 +66,9 @@ class Logger { } // namespace odr +// `message` is a streaming expression (e.g. `"x=" << x`), so it deliberately +// must not be parenthesized. +// NOLINTBEGIN(bugprone-macro-parentheses) #define ODR_LOG(logger, level, message) \ do { \ if ((logger).will_log(level)) { \ @@ -74,6 +77,7 @@ class Logger { (logger).log(level, ss.str()); \ } \ } while (0) +// NOLINTEND(bugprone-macro-parentheses) #define ODR_VERBOSE(logger, message) ODR_LOG(logger, LogLevel::verbose, message) #define ODR_DEBUG(logger, message) ODR_LOG(logger, LogLevel::debug, message) #define ODR_INFO(logger, message) ODR_LOG(logger, LogLevel::info, message) diff --git a/src/odr/odr.cpp b/src/odr/odr.cpp index 7e868c0f3..5e72aae36 100644 --- a/src/odr/odr.cpp +++ b/src/odr/odr.cpp @@ -6,13 +6,9 @@ #include #include -std::string odr::version() noexcept { - return internal::project_info::version(); -} +std::string odr::version() { return internal::project_info::version(); } -std::string odr::commit_hash() noexcept { - return internal::git_info::commit_hash(); -} +std::string odr::commit_hash() { return internal::git_info::commit_hash(); } bool odr::is_dirty() noexcept { return internal::git_info::is_dirty(); } @@ -163,7 +159,7 @@ odr::document_type_by_file_type(const FileType type) noexcept { } } -std::string odr::file_type_to_string(const FileType type) noexcept { +std::string odr::file_type_to_string(const FileType type) { switch (type) { case FileType::unknown: return "unknown"; @@ -218,7 +214,7 @@ std::string odr::file_type_to_string(const FileType type) noexcept { } } -std::string odr::file_category_to_string(const FileCategory type) noexcept { +std::string odr::file_category_to_string(const FileCategory type) { switch (type) { case FileCategory::unknown: return "unknown"; @@ -235,7 +231,7 @@ std::string odr::file_category_to_string(const FileCategory type) noexcept { } } -std::string odr::document_type_to_string(const DocumentType type) noexcept { +std::string odr::document_type_to_string(const DocumentType type) { switch (type) { case DocumentType::unknown: return "unknown"; diff --git a/src/odr/odr.hpp b/src/odr/odr.hpp index bfd8c094a..1e6045ece 100644 --- a/src/odr/odr.hpp +++ b/src/odr/odr.hpp @@ -15,10 +15,10 @@ enum class DocumentType; /// @brief Get the version of the library. /// @return The version of the library. -[[nodiscard]] std::string version() noexcept; +[[nodiscard]] std::string version(); /// @brief Get the commit hash of the library. /// @return The commit hash of the library. -[[nodiscard]] std::string commit_hash() noexcept; +[[nodiscard]] std::string commit_hash(); /// @brief Check if the library is dirty (i.e., has uncommitted changes). /// @return True if the library is dirty, false otherwise. [[nodiscard]] bool is_dirty() noexcept; @@ -45,15 +45,15 @@ file_type_by_file_extension(const std::string &extension) noexcept; /// @brief Get the file type as a string. /// @param type The file type. /// @return The file type as a string. -[[nodiscard]] std::string file_type_to_string(FileType type) noexcept; +[[nodiscard]] std::string file_type_to_string(FileType type); /// @brief Get the file category as a string. /// @param type The file type. /// @return The file type as a string. -[[nodiscard]] std::string file_category_to_string(FileCategory type) noexcept; +[[nodiscard]] std::string file_category_to_string(FileCategory type); /// @brief Get the document type as a string. /// @param type The file type. /// @return The file type as a string. -[[nodiscard]] std::string document_type_to_string(DocumentType type) noexcept; +[[nodiscard]] std::string document_type_to_string(DocumentType type); /// @brief Get the file type by the MIME type. /// @param mimetype The MIME type. /// @return The file type. diff --git a/test/.clang-tidy b/test/.clang-tidy new file mode 100644 index 000000000..e27fa2021 --- /dev/null +++ b/test/.clang-tidy @@ -0,0 +1,7 @@ +--- +# Test code establishes optional state through gtest assertion macros +# (e.g. ASSERT_TRUE(opt.has_value())) that clang-tidy's dataflow analysis +# cannot see through, so bugprone-unchecked-optional-access reports only false +# positives here. Everything else from the top-level config still applies. +InheritParentConfig: true +Checks: '-bugprone-unchecked-optional-access' diff --git a/test/src/html_output_test.cpp b/test/src/html_output_test.cpp index b15cab823..d082f49b3 100644 --- a/test/src/html_output_test.cpp +++ b/test/src/html_output_test.cpp @@ -90,8 +90,10 @@ TEST_P(HtmlOutputTests, html_meta) { { const std::string meta_output = output_path + "/meta.json"; const nlohmann::json json = util::meta::meta_to_json(file_meta); - std::ofstream o(meta_output); - o << std::setw(4) << json << std::endl; + { + std::ofstream o(meta_output); + o << std::setw(4) << json << '\n'; + } EXPECT_TRUE(fs::is_regular_file(meta_output)); EXPECT_LT(0, fs::file_size(meta_output)); } @@ -141,8 +143,10 @@ TEST_P(HtmlOutputTests, html_meta) { { const std::string meta_output = output_path + "/meta-decrypted.json"; const nlohmann::json json = util::meta::meta_to_json(file_meta); - std::ofstream o(meta_output); - o << std::setw(4) << json << std::endl; + { + std::ofstream o(meta_output); + o << std::setw(4) << json << '\n'; + } EXPECT_TRUE(fs::is_regular_file(meta_output)); EXPECT_LT(0, fs::file_size(meta_output)); } diff --git a/test/src/internal/pdf/pdf_file_parser.cpp b/test/src/internal/pdf/pdf_file_parser.cpp index 928223c19..7a27d7313 100644 --- a/test/src/internal/pdf/pdf_file_parser.cpp +++ b/test/src/internal/pdf/pdf_file_parser.cpp @@ -32,46 +32,45 @@ TEST(FileParser, foo) { const auto &[size, dictionary] = entry.as_trailer(); for (const auto &key : dictionary | std::views::keys) { - std::cout << key << std::endl; + std::cout << key << '\n'; } } if (entry.is_object()) { const IndirectObject &object = entry.as_object(); std::cout << "object " << object.reference.id << " " - << object.reference.gen << std::endl; + << object.reference.gen << '\n'; if (object.object.is_integer()) { - std::cout << "integer " << object.object.as_integer() << std::endl; + std::cout << "integer " << object.object.as_integer() << '\n'; } if (object.object.is_array()) { - std::cout << "array size " << object.object.as_array().size() - << std::endl; + std::cout << "array size " << object.object.as_array().size() << '\n'; } if (object.object.is_dictionary()) { const auto &dictionary = object.object.as_dictionary(); - std::cout << "dictionary size " << dictionary.size() << std::endl; + std::cout << "dictionary size " << dictionary.size() << '\n'; for (const auto &key : dictionary | std::views::keys) { - std::cout << key << std::endl; + std::cout << key << '\n'; } } if (object.has_stream) { const Dictionary &dictionary = object.object.as_dictionary(); std::string stream = parser.read_stream(-1); - std::cout << stream.size() << std::endl; + std::cout << stream.size() << '\n'; if (dictionary.has_key("Filter")) { const std::string &filter = dictionary["Filter"].as_string(); - std::cout << filter << std::endl; + std::cout << filter << '\n'; if (filter == "FlateDecode") { std::string inflated = crypto::util::zlib_inflate(stream); - std::cout << inflated.size() << std::endl; + std::cout << inflated.size() << '\n'; } } } - std::cout << std::endl; + std::cout << '\n'; } } }