Skip to content

Commit

Permalink
refactor(util): add derived_cast helper
Browse files Browse the repository at this point in the history
static_cast has multiple meanings. Make it clearer that some casts are
for downcasting only by implementing a derived_cast function.
  • Loading branch information
strager committed Oct 31, 2023
1 parent 645999a commit 3dd09c6
Show file tree
Hide file tree
Showing 7 changed files with 33 additions and 14 deletions.
2 changes: 1 addition & 1 deletion src/quick-lint-js/diag/diagnostic-formatter.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ inline void Diagnostic_Formatter<Derived>::format_message(
static constexpr auto npos = String8_View::npos;
using String8_Pos = String8_View::size_type;

Derived* self = static_cast<Derived*>(this);
Derived* self = derived_cast<Derived*>(this);

Source_Code_Span origin_span =
get_argument_source_code_span(args, diagnostic, 0);
Expand Down
12 changes: 4 additions & 8 deletions src/quick-lint-js/fe/expression.h
Original file line number Diff line number Diff line change
Expand Up @@ -304,34 +304,30 @@ template <class Derived>
Derived expression_cast(Expression *p) {
// TODO(strager): Assert that Derived matches the Expression's run-time
// type.
return static_cast<Derived>(p);
return derived_cast<Derived>(p);
}

template <class Derived>
Derived expression_cast(const Expression *p) {
// TODO(strager): Assert that Derived matches the Expression's run-time
// type.
return static_cast<Derived>(p);
return derived_cast<Derived>(p);
}

template <class Derived>
Derived expression_cast(Expression &p) {
// TODO(strager): Assert that Derived matches the Expression's run-time
// type.
return static_cast<Derived>(p);
return derived_cast<Derived>(p);
}

template <class Derived>
Derived expression_cast(const Expression &p) {
// TODO(strager): Assert that Derived matches the Expression's run-time
// type.
return static_cast<Derived>(p);
return derived_cast<Derived>(p);
}

// Prevent expression_cast<array>((call*)p).
template <class Derived, class Expression>
Derived *expression_cast(Expression *) = delete;

template <class Expression, class... Args>
Expression *Expression_Arena::make_expression(Args &&... args) {
Expression *result(this->allocate<Expression>(std::forward<Args>(args)...));
Expand Down
4 changes: 2 additions & 2 deletions src/quick-lint-js/fe/lex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1126,7 +1126,7 @@ Lexer_Transaction Lexer::begin_transaction() {

void Lexer::commit_transaction(Lexer_Transaction&& transaction) {
Buffering_Diag_Reporter* buffered_diagnostics =
static_cast<Buffering_Diag_Reporter*>(this->diag_reporter_);
derived_cast<Buffering_Diag_Reporter*>(this->diag_reporter_);
buffered_diagnostics->move_into(transaction.old_diag_reporter);

this->diag_reporter_ = transaction.old_diag_reporter;
Expand All @@ -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<Buffering_Diag_Reporter*>(this->diag_reporter_);
derived_cast<Buffering_Diag_Reporter*>(this->diag_reporter_);
return !buffered_diagnostics->empty();
}

Expand Down
2 changes: 1 addition & 1 deletion src/quick-lint-js/fe/parse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -852,7 +852,7 @@ Parser_Transaction Parser::begin_transaction() {

void Parser::commit_transaction(Parser_Transaction&& transaction) {
auto* buffered_diagnostics =
static_cast<Buffering_Diag_Reporter*>(this->diag_reporter_);
derived_cast<Buffering_Diag_Reporter*>(this->diag_reporter_);
buffered_diagnostics->move_into(transaction.old_diag_reporter);
this->diag_reporter_ = transaction.old_diag_reporter;

Expand Down
2 changes: 1 addition & 1 deletion src/quick-lint-js/io/file-canonical.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,7 @@ class Path_Canonicalizer_Base {
return component;
}

Derived &derived() { return *static_cast<Derived *>(this); }
Derived &derived() { return *derived_cast<Derived *>(this); }

protected:
void skip_to_next_component() {
Expand Down
2 changes: 1 addition & 1 deletion src/quick-lint-js/lsp/lsp-server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<LSP_Documents::Lintable_Document&>(doc),
this->linter_.lint(derived_cast<LSP_Documents::Lintable_Document&>(doc),
notification.uri.json, this->outgoing_messages_);
break;

Expand Down
23 changes: 23 additions & 0 deletions src/quick-lint-js/util/cast.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,29 @@ template <class Enum>
constexpr std::underlying_type_t<Enum> enum_to_int_cast(Enum value) {
return static_cast<std::underlying_type_t<Enum>>(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 <class Derived_Pointer, class Base>
Derived_Pointer derived_cast(Base* base) {
using Derived = std::remove_pointer_t<Derived_Pointer>;
static_assert(std::is_base_of_v<Base, Derived>,
"Derived should derive from Base");
static_assert(!std::is_base_of_v<Derived, Base>,
"Derived should not be the same type as Base");
return static_cast<Derived_Pointer>(base);
}
template <class Derived_Reference, class Base>
Derived_Reference derived_cast(Base& base) {
using Derived = std::remove_reference_t<Derived_Reference>;
static_assert(std::is_base_of_v<Base, Derived>,
"Derived should derive from Base");
static_assert(!std::is_base_of_v<Derived, Base>,
"Derived should not be the same type as Base");
return static_cast<Derived_Reference>(base);
}
}

// quick-lint-js finds bugs in JavaScript programs.
Expand Down

0 comments on commit 3dd09c6

Please sign in to comment.