From 3dd09c63e9501721767230c36ec676a5b6c7c454 Mon Sep 17 00:00:00 2001 From: "Matthew \"strager\" Glazar" Date: Tue, 31 Oct 2023 16:51:06 -0400 Subject: [PATCH] refactor(util): add derived_cast helper static_cast has multiple meanings. Make it clearer that some casts are for downcasting only by implementing a derived_cast function. --- src/quick-lint-js/diag/diagnostic-formatter.h | 2 +- src/quick-lint-js/fe/expression.h | 12 ++++------ src/quick-lint-js/fe/lex.cpp | 4 ++-- src/quick-lint-js/fe/parse.cpp | 2 +- src/quick-lint-js/io/file-canonical.cpp | 2 +- src/quick-lint-js/lsp/lsp-server.cpp | 2 +- src/quick-lint-js/util/cast.h | 23 +++++++++++++++++++ 7 files changed, 33 insertions(+), 14 deletions(-) diff --git a/src/quick-lint-js/diag/diagnostic-formatter.h b/src/quick-lint-js/diag/diagnostic-formatter.h index 6ceadbf142..32f0e2412c 100644 --- a/src/quick-lint-js/diag/diagnostic-formatter.h +++ b/src/quick-lint-js/diag/diagnostic-formatter.h @@ -95,7 +95,7 @@ inline void Diagnostic_Formatter::format_message( static constexpr auto npos = String8_View::npos; using String8_Pos = String8_View::size_type; - Derived* self = static_cast(this); + Derived* self = derived_cast(this); Source_Code_Span origin_span = get_argument_source_code_span(args, diagnostic, 0); diff --git a/src/quick-lint-js/fe/expression.h b/src/quick-lint-js/fe/expression.h index e1fc8e2838..1e0b852b96 100644 --- a/src/quick-lint-js/fe/expression.h +++ b/src/quick-lint-js/fe/expression.h @@ -304,34 +304,30 @@ template Derived expression_cast(Expression *p) { // TODO(strager): Assert that Derived matches the Expression's run-time // type. - return static_cast(p); + return derived_cast(p); } template Derived expression_cast(const Expression *p) { // TODO(strager): Assert that Derived matches the Expression's run-time // type. - return static_cast(p); + return derived_cast(p); } template Derived expression_cast(Expression &p) { // TODO(strager): Assert that Derived matches the Expression's run-time // type. - return static_cast(p); + return derived_cast(p); } template Derived expression_cast(const Expression &p) { // TODO(strager): Assert that Derived matches the Expression's run-time // type. - return static_cast(p); + return derived_cast(p); } -// Prevent expression_cast((call*)p). -template -Derived *expression_cast(Expression *) = delete; - template Expression *Expression_Arena::make_expression(Args &&... args) { Expression *result(this->allocate(std::forward(args)...)); diff --git a/src/quick-lint-js/fe/lex.cpp b/src/quick-lint-js/fe/lex.cpp index fb62369534..1973613490 100644 --- a/src/quick-lint-js/fe/lex.cpp +++ b/src/quick-lint-js/fe/lex.cpp @@ -1126,7 +1126,7 @@ Lexer_Transaction Lexer::begin_transaction() { void Lexer::commit_transaction(Lexer_Transaction&& transaction) { Buffering_Diag_Reporter* buffered_diagnostics = - static_cast(this->diag_reporter_); + derived_cast(this->diag_reporter_); buffered_diagnostics->move_into(transaction.old_diag_reporter); this->diag_reporter_ = transaction.old_diag_reporter; @@ -1146,7 +1146,7 @@ void Lexer::roll_back_transaction(Lexer_Transaction&& transaction) { bool Lexer::transaction_has_lex_diagnostics(const Lexer_Transaction&) const { Buffering_Diag_Reporter* buffered_diagnostics = - static_cast(this->diag_reporter_); + derived_cast(this->diag_reporter_); return !buffered_diagnostics->empty(); } diff --git a/src/quick-lint-js/fe/parse.cpp b/src/quick-lint-js/fe/parse.cpp index 723e56cac5..90ef2a0ae3 100644 --- a/src/quick-lint-js/fe/parse.cpp +++ b/src/quick-lint-js/fe/parse.cpp @@ -852,7 +852,7 @@ Parser_Transaction Parser::begin_transaction() { void Parser::commit_transaction(Parser_Transaction&& transaction) { auto* buffered_diagnostics = - static_cast(this->diag_reporter_); + derived_cast(this->diag_reporter_); buffered_diagnostics->move_into(transaction.old_diag_reporter); this->diag_reporter_ = transaction.old_diag_reporter; diff --git a/src/quick-lint-js/io/file-canonical.cpp b/src/quick-lint-js/io/file-canonical.cpp index b93fb32e80..9eee6132d9 100644 --- a/src/quick-lint-js/io/file-canonical.cpp +++ b/src/quick-lint-js/io/file-canonical.cpp @@ -413,7 +413,7 @@ class Path_Canonicalizer_Base { return component; } - Derived &derived() { return *static_cast(this); } + Derived &derived() { return *derived_cast(this); } protected: void skip_to_next_component() { diff --git a/src/quick-lint-js/lsp/lsp-server.cpp b/src/quick-lint-js/lsp/lsp-server.cpp index bb61bdc40e..d862ee4519 100644 --- a/src/quick-lint-js/lsp/lsp-server.cpp +++ b/src/quick-lint-js/lsp/lsp-server.cpp @@ -374,7 +374,7 @@ void Linting_LSP_Server_Handler::handle_text_document_did_change_notification( } case LSP_Documents::Document_Type::lintable: - this->linter_.lint(static_cast(doc), + this->linter_.lint(derived_cast(doc), notification.uri.json, this->outgoing_messages_); break; diff --git a/src/quick-lint-js/util/cast.h b/src/quick-lint-js/util/cast.h index 66ce118c30..278e387b9b 100644 --- a/src/quick-lint-js/util/cast.h +++ b/src/quick-lint-js/util/cast.h @@ -55,6 +55,29 @@ template constexpr std::underlying_type_t enum_to_int_cast(Enum value) { return static_cast>(value); } + +// Cast a pointer or reference to a pointer/reference to a derived class. +// +// In the presence of multiple inheritance, perform pointer adjustments (like +// what static_cast does). +template +Derived_Pointer derived_cast(Base* base) { + using Derived = std::remove_pointer_t; + static_assert(std::is_base_of_v, + "Derived should derive from Base"); + static_assert(!std::is_base_of_v, + "Derived should not be the same type as Base"); + return static_cast(base); +} +template +Derived_Reference derived_cast(Base& base) { + using Derived = std::remove_reference_t; + static_assert(std::is_base_of_v, + "Derived should derive from Base"); + static_assert(!std::is_base_of_v, + "Derived should not be the same type as Base"); + return static_cast(base); +} } // quick-lint-js finds bugs in JavaScript programs.