Skip to content

Commit 51e46bd

Browse files
committed
handle subassignemnts with the same object
1 parent 22d94ed commit 51e46bd

File tree

2 files changed

+186
-1
lines changed

2 files changed

+186
-1
lines changed

R/utils-get_code_dependency.R

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,18 @@ extract_occurrence <- function(pd) {
306306
ans <- move_functions_after_arrow(ans, unique(x[sym_fc_cond, "text"]))
307307
roll <- in_parenthesis(pd)
308308
if (length(roll)) {
309-
c(setdiff(ans, roll), roll)
309+
# detect elements appread in parenthesis and move them on RHS
310+
# but only their first appearance
311+
# as the same object can appear as regular object and the one used in parenthesis
312+
result <- ans
313+
for (elem in roll) {
314+
idx <- which(result == elem)[1]
315+
if (!is.na(idx)) {
316+
result <- result[-idx]
317+
}
318+
}
319+
c(result, roll)
320+
310321
} else {
311322
ans
312323
}
@@ -330,6 +341,9 @@ move_functions_after_arrow <- function(ans, functions) {
330341
if (length(arrow_pos) == 0) {
331342
return(ans)
332343
}
344+
if (length(functions) == 0) {
345+
return(ans)
346+
}
333347
before_arrow <- setdiff(ans[1:arrow_pos], functions)
334348
after_arrow <- ans[(arrow_pos + 1):length(ans)]
335349
c(before_arrow, unique(c(intersect(ans[1:arrow_pos], functions), after_arrow)))

tests/testthat/test-utils-get_code_dependency.R

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,3 +86,174 @@ testthat::describe("get_code with multiple assignments inside an expression", {
8686
testthat::expect_equal(get_code(td, names = "var2"), code_source)
8787
})
8888
})
89+
90+
testthat::describe("get_code with subassignments", {
91+
testthat::it("tracks $ subassignment as producing the base object", {
92+
td <- qenv() |>
93+
within({
94+
iris <- iris
95+
iris$Species[sample.int(nrow(iris), 50)] <- NA
96+
})
97+
98+
code_source <- "iris <- iris\niris$Species[sample.int(nrow(iris), 50)] <- NA"
99+
100+
testthat::expect_equal(get_code(td, names = "iris"), code_source)
101+
})
102+
103+
testthat::it("tracks [ subassignment as producing the base object", {
104+
td <- qenv() |>
105+
within({
106+
x <- 1:10
107+
x[1:3] <- c(10, 20, 30)
108+
})
109+
110+
code_source <- "x <- 1:10\nx[1:3] <- c(10, 20, 30)"
111+
112+
testthat::expect_equal(get_code(td, names = "x"), code_source)
113+
})
114+
115+
testthat::it("tracks [[ subassignment as producing the base object", {
116+
td <- qenv() |>
117+
within({
118+
lst <- list(a = 1, b = 2)
119+
lst[["c"]] <- 3
120+
})
121+
122+
code_source <- "lst <- list(a = 1, b = 2)\nlst[[\"c\"]] <- 3"
123+
124+
testthat::expect_equal(get_code(td, names = "lst"), code_source)
125+
})
126+
127+
testthat::it("tracks nested subassignments", {
128+
td <- qenv() |>
129+
within({
130+
df <- data.frame(x = 1:5, y = 6:10)
131+
df$x[df$y > 8] <- 99
132+
})
133+
134+
code_source <- "df <- data.frame(x = 1:5, y = 6:10)\ndf$x[df$y > 8] <- 99"
135+
136+
testthat::expect_equal(get_code(td, names = "df"), code_source)
137+
})
138+
139+
testthat::it("tracks multiple subassignments to same object", {
140+
td <- qenv() |>
141+
within({
142+
iris <- iris
143+
iris$Species[sample.int(nrow(iris), 10)] <- NA
144+
iris$Sepal.Length[1:5] <- 0
145+
})
146+
147+
code_source <- "iris <- iris\niris$Species[sample.int(nrow(iris), 10)] <- NA\niris$Sepal.Length[1:5] <- 0"
148+
149+
testthat::expect_equal(get_code(td, names = "iris"), code_source)
150+
})
151+
152+
testthat::it("tracks subassignments with complex expressions", {
153+
td <- qenv() |>
154+
within({
155+
mat <- matrix(1:12, nrow = 3)
156+
mat[mat > 5 & mat < 10] <- 0
157+
})
158+
159+
code_source <- "mat <- matrix(1:12, nrow = 3)\nmat[mat > 5 & mat < 10] <- 0"
160+
161+
testthat::expect_equal(get_code(td, names = "mat"), code_source)
162+
})
163+
164+
testthat::it("tracks subassignments with function calls on LHS", {
165+
td <- qenv() |>
166+
within({
167+
lst <- list(a = 1, b = 2)
168+
names(lst)[1] <- "first"
169+
})
170+
171+
code_source <- "lst <- list(a = 1, b = 2)\nnames(lst)[1] <- \"first\""
172+
173+
testthat::expect_equal(get_code(td, names = "lst"), code_source)
174+
})
175+
176+
testthat::it("tracks -> operator with subassignments", {
177+
td <- qenv() |>
178+
within({
179+
x <- 1:10
180+
c(10, 20, 30) -> x[1:3] # nolint: assignment.
181+
})
182+
183+
code_source <- "x <- 1:10\nx[1:3] <- c(10, 20, 30)"
184+
185+
testthat::expect_equal(get_code(td, names = "x"), code_source)
186+
})
187+
188+
testthat::it("tracks attributes() function with subassignments", {
189+
td <- qenv() |>
190+
within({
191+
x <- 1:5
192+
attributes(x)$names <- letters[1:5]
193+
})
194+
195+
code_source <- "x <- 1:5\nattributes(x)$names <- letters[1:5]"
196+
197+
testthat::expect_equal(get_code(td, names = "x"), code_source)
198+
})
199+
200+
testthat::it("handles complex nested subassignments", {
201+
td <- qenv() |>
202+
within({
203+
df <- data.frame(x = 1:5, y = 6:10)
204+
df[df$x > 2, "y"][1:2] <- c(99, 100)
205+
})
206+
207+
code_source <- "df <- data.frame(x = 1:5, y = 6:10)\ndf[df$x > 2, \"y\"][1:2] <- c(99, 100)"
208+
209+
testthat::expect_equal(get_code(td, names = "df"), code_source)
210+
})
211+
212+
testthat::it("handles subassignments with multiple operators", {
213+
td <- qenv() |>
214+
within({
215+
lst <- list(a = list(b = 1, c = 2))
216+
lst$a$b[2] <- 99
217+
})
218+
219+
code_source <- "lst <- list(a = list(b = 1, c = 2))\nlst$a$b[2] <- 99"
220+
221+
testthat::expect_equal(get_code(td, names = "lst"), code_source)
222+
})
223+
224+
testthat::it("handles subassignments with data frame column creation", {
225+
td <- qenv() |>
226+
within({
227+
df <- data.frame(x = 1:3)
228+
df$new_col <- c("a", "b", "c")
229+
})
230+
231+
code_source <- "df <- data.frame(x = 1:3)\ndf$new_col <- c(\"a\", \"b\", \"c\")"
232+
233+
testthat::expect_equal(get_code(td, names = "df"), code_source)
234+
})
235+
236+
testthat::it("handles subassignments with matrix indexing", {
237+
td <- qenv() |>
238+
within({
239+
mat <- matrix(1:9, nrow = 3)
240+
mat[1:2, 2:3] <- matrix(0, nrow = 2, ncol = 2)
241+
})
242+
243+
code_source <- "mat <- matrix(1:9, nrow = 3)\nmat[1:2, 2:3] <- matrix(0, nrow = 2, ncol = 2)"
244+
245+
testthat::expect_equal(get_code(td, names = "mat"), code_source)
246+
})
247+
248+
testthat::it("handles subassignments with logical indexing", {
249+
td <- qenv() |>
250+
within({
251+
vec <- 1:10
252+
vec[vec %% 2 == 0] <- vec[vec %% 2 == 0] * 2
253+
})
254+
255+
code_source <- "vec <- 1:10\nvec[vec %% 2 == 0] <- vec[vec %% 2 == 0] * 2"
256+
257+
testthat::expect_equal(get_code(td, names = "vec"), code_source)
258+
})
259+
})

0 commit comments

Comments
 (0)