diff --git a/.gitignore b/.gitignore index 014c1b316..10ec9a9ad 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,5 @@ docs .clangd launch.json .vscode/ +.cache/ +compile_commands.json diff --git a/NEWS.md b/NEWS.md index 2cb290516..fc859b73c 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,8 @@ # vctrs (development version) +* `vec_detect_complete(NULL)` now returns `logical()`, consistent with + `vec_detect_missing(NULL)` (#1916). + # vctrs 0.6.5 * Internal changes requested by CRAN around C level format strings (#1896). diff --git a/src/complete.c b/src/complete.c index 1723f0c21..db7a47970 100644 --- a/src/complete.c +++ b/src/complete.c @@ -89,7 +89,8 @@ void vec_detect_complete_switch(SEXP x, R_len_t size, int* p_out) { case VCTRS_TYPE_raw: raw_detect_complete(x, size, p_out); break; case VCTRS_TYPE_list: list_detect_complete(x, size, p_out); break; case VCTRS_TYPE_dataframe: df_detect_complete(x, size, p_out); break; - case VCTRS_TYPE_scalar: r_stop_internal("Can't detect missing values in scalars."); + case VCTRS_TYPE_null: break; + case VCTRS_TYPE_scalar: stop_scalar_type(x, vec_args.empty, r_lazy_null); default: stop_unimplemented_vctrs_type("vec_detect_complete", vec_proxy_typeof(x)); } } @@ -169,12 +170,31 @@ void list_detect_complete(SEXP x, R_len_t size, int* p_out) { // ----------------------------------------------------------------------------- +static inline void col_detect_complete_switch(SEXP x, R_len_t size, int* p_out); + static inline void df_detect_complete(SEXP x, R_len_t size, int* p_out) { r_ssize n_cols = r_length(x); const SEXP* p_x = VECTOR_PTR_RO(x); for (r_ssize i = 0; i < n_cols; ++i) { - vec_detect_complete_switch(p_x[i], size, p_out); + col_detect_complete_switch(p_x[i], size, p_out); + } +} + +static inline +void col_detect_complete_switch(SEXP x, R_len_t size, int* p_out) { + switch (vec_proxy_typeof(x)) { + case VCTRS_TYPE_logical: lgl_detect_complete(x, size, p_out); break; + case VCTRS_TYPE_integer: int_detect_complete(x, size, p_out); break; + case VCTRS_TYPE_double: dbl_detect_complete(x, size, p_out); break; + case VCTRS_TYPE_complex: cpl_detect_complete(x, size, p_out); break; + case VCTRS_TYPE_character: chr_detect_complete(x, size, p_out); break; + case VCTRS_TYPE_raw: raw_detect_complete(x, size, p_out); break; + case VCTRS_TYPE_list: list_detect_complete(x, size, p_out); break; + case VCTRS_TYPE_dataframe: r_stop_internal("Data frame columns should have been flattened by now."); + case VCTRS_TYPE_null: r_abort("Unexpected `NULL` column found in a data frame."); + case VCTRS_TYPE_scalar: stop_scalar_type(x, vec_args.empty, r_lazy_null); + default: stop_unimplemented_vctrs_type("vec_detect_complete", vec_proxy_typeof(x)); } } diff --git a/tests/testthat/_snaps/complete.md b/tests/testthat/_snaps/complete.md new file mode 100644 index 000000000..954f505fa --- /dev/null +++ b/tests/testthat/_snaps/complete.md @@ -0,0 +1,16 @@ +# catches `NULL` data frame columns + + Code + vec_detect_complete(df) + Condition + Error in `vec_detect_complete()`: + ! Unexpected `NULL` column found in a data frame. + +# catches scalar objects + + Code + vec_detect_complete(lm(1 ~ 1)) + Condition + Error in `vec_size()`: + ! `x` must be a vector, not a object. + diff --git a/tests/testthat/_snaps/rank.md b/tests/testthat/_snaps/rank.md index cc7ceef89..589704f58 100644 --- a/tests/testthat/_snaps/rank.md +++ b/tests/testthat/_snaps/rank.md @@ -1,3 +1,19 @@ +# `x` must not be `NULL` (#1823) + + Code + vec_rank(NULL) + Condition + Error: + ! This type is not supported by `vec_order()`. + +--- + + Code + vec_rank(NULL, incomplete = "na") + Condition + Error: + ! This type is not supported by `vec_order()`. + # `ties` is validated Code diff --git a/tests/testthat/test-complete.R b/tests/testthat/test-complete.R index 0ca0ca4a6..12f4ccfde 100644 --- a/tests/testthat/test-complete.R +++ b/tests/testthat/test-complete.R @@ -113,3 +113,22 @@ test_that("works with arrays", { expect_identical(vec_detect_complete(x), c(TRUE, FALSE)) expect_identical(vec_detect_complete(y), c(TRUE, FALSE)) }) + +test_that("works with `NULL`", { + # Consistent with `vec_detect_missing()` + expect_identical(vec_detect_complete(NULL), logical()) +}) + +test_that("catches `NULL` data frame columns", { + df <- new_data_frame(list(x = integer(), y = NULL), n = 0L) + + expect_snapshot(error = TRUE, { + vec_detect_complete(df) + }) +}) + +test_that("catches scalar objects", { + expect_snapshot(error = TRUE, { + vec_detect_complete(lm(1 ~ 1)) + }) +}) diff --git a/tests/testthat/test-rank.R b/tests/testthat/test-rank.R index 558173e8a..866d0625d 100644 --- a/tests/testthat/test-rank.R +++ b/tests/testthat/test-rank.R @@ -141,6 +141,15 @@ test_that("`x` must be a vector", { expect_error(vec_rank(identity), class = "vctrs_error_scalar_type") }) +test_that("`x` must not be `NULL` (#1823)", { + expect_snapshot(error = TRUE, { + vec_rank(NULL) + }) + expect_snapshot(error = TRUE, { + vec_rank(NULL, incomplete = "na") + }) +}) + test_that("`ties` is validated", { expect_snapshot(error = TRUE, vec_rank(1, ties = "foo")) expect_snapshot(error = TRUE, vec_rank(1, ties = 1))