Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better checking of list arguments to bind_rows() #3068

Merged
merged 6 commits into from
Sep 5, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

* Semi- and anti-joins now preserve the order of left-hand-side data frame (#3089).

* Improved error message for invalid list arguments to `bind_rows()` (#3068).

* Grouping by character vectors is now faster (#2204).

# dplyr 0.7.2
Expand Down
33 changes: 13 additions & 20 deletions src/bind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,34 +79,28 @@ void inner_vector_check(SEXP x, int nrows, int arg) {
}
}

static
bool is_non_data_frame_object(SEXP x) {
if (TYPEOF(x) != VECSXP) return false;
if (!OBJECT(x)) return false;
return !Rf_inherits(x, "data.frame");
}

static
void rbind_vector_check(SEXP x, R_xlen_t nrows, int arg) {
if (!is_vector(x) || is_non_data_frame_object(x)) {
bad_pos_arg(arg + 1, "must be a data frame or a named atomic vector, not a {type}",
_["type"] = get_single_class(x));
}

if (rows_length(x, true) != nrows) {
bad_pos_arg(arg + 1, "must be length {expected_size}, not {actual_size}",
_["expected_size"] = rows_length(x, true), _["actual_size"] = nrows);
}

switch (TYPEOF(x)) {
case LGLSXP:
case INTSXP:
case REALSXP:
case CPLXSXP:
case STRSXP:
case RAWSXP: {
if (vec_names(x) != R_NilValue)
return;
if (vec_names(x) == R_NilValue) {
bad_pos_arg(arg + 1, "must have names");
}
case VECSXP: {
if (!OBJECT(x) || Rf_inherits(x, "data.frame"))
return;
break;
}
default:
break;
}
bad_pos_arg(arg + 1, "must be a data frame or a named atomic vector, not a {type}",
_["type"] = get_single_class(x));
}
static
void cbind_vector_check(SEXP x, R_xlen_t nrows, SEXP contr, int arg) {
Expand Down Expand Up @@ -206,7 +200,6 @@ List rbind__impl(List dots, const SymbolString& id) {
SymbolVector names;

k = 0;
Function enc2native("enc2native");
for (int i = 0; i < ndata; i++) {
Rcpp::checkUserInterrupt();

Expand Down
10 changes: 8 additions & 2 deletions tests/testthat/test-binds.R
Original file line number Diff line number Diff line change
Expand Up @@ -115,11 +115,17 @@ test_that("bind_rows ignores NULL", {
expect_equal(bind_rows(list(df, NULL)), df)
})

test_that("bind_rows only accepts data frames or vectors", {
test_that("bind_rows only accepts data frames or named vectors", {
ll <- list(1:5, rlang::get_env())
expect_error(
bind_rows(ll),
"Argument 2 is a list, must contain atomic vectors",
"Argument 1 must have names",
fixed = TRUE
)
ll <- list(tibble(a = 1:5), rlang::get_env())
expect_error(
bind_rows(ll),
"Argument 2 must be a data frame or a named atomic vector, not a environment",
fixed = TRUE
)
})
Expand Down