From 29db38fb823fcfe9cf2ad05f885bf20dfa1c964b Mon Sep 17 00:00:00 2001
From: Hadley Wickham <h.wickham@gmail.com>
Date: Fri, 20 Sep 2019 14:37:23 -0500
Subject: [PATCH] Revamp operation of rd and namespace roclets.

At the heart of both roclets is now a loop that processes each tag in turn, calling `roxy_tag_rd()` or `roxy_tag_ns()`. This makes it easier to closely connected the parsing and processing tag, making the pieces easier to understand.

Other changes:

* `@docType package` no longer adds `-package` alias as this broke
   encapsulation and was only mildly useful.

* .rexports, .formals, and .methods all now work by defining internal
  tags in object_defaults.

Fixes #915
---
 NAMESPACE                           |  46 +++
 NEWS.md                             |   4 +
 R/namespace.R                       | 243 +++++++++------
 R/object-defaults.R                 |  21 +-
 R/rd-examples.R                     |  40 +--
 R/rd-include-rmd.R                  |   3 +-
 R/rd-inherit.R                      |  36 +++
 R/rd.R                              | 462 +++++++++++++---------------
 R/tag.R                             |   9 +-
 R/topic.R                           |  10 +
 man/roclet.Rd                       |   4 +-
 man/roxy_tag.Rd                     |   3 +-
 tests/testthat/test-block-print.txt |   1 +
 tests/testthat/test-rd-doctype.R    |  22 --
 14 files changed, 510 insertions(+), 394 deletions(-)

diff --git a/NAMESPACE b/NAMESPACE
index 3c0b6b719..de5f300a1 100644
--- a/NAMESPACE
+++ b/NAMESPACE
@@ -51,6 +51,7 @@ S3method(merge,roxy_field_minidesc)
 S3method(merge,roxy_field_param)
 S3method(merge,roxy_field_reexport)
 S3method(merge,roxy_field_section)
+S3method(object_defaults,"function")
 S3method(object_defaults,data)
 S3method(object_defaults,default)
 S3method(object_defaults,import)
@@ -74,6 +75,19 @@ S3method(roclet_preprocess,roclet_namespace)
 S3method(roclet_process,roclet_namespace)
 S3method(roclet_process,roclet_rd)
 S3method(roclet_process,roclet_vignette)
+S3method(roxy_tag_ns,default)
+S3method(roxy_tag_ns,roxy_tag_evalNamespace)
+S3method(roxy_tag_ns,roxy_tag_export)
+S3method(roxy_tag_ns,roxy_tag_exportClass)
+S3method(roxy_tag_ns,roxy_tag_exportMethod)
+S3method(roxy_tag_ns,roxy_tag_exportPattern)
+S3method(roxy_tag_ns,roxy_tag_exportS3Method)
+S3method(roxy_tag_ns,roxy_tag_import)
+S3method(roxy_tag_ns,roxy_tag_importClassesFrom)
+S3method(roxy_tag_ns,roxy_tag_importFrom)
+S3method(roxy_tag_ns,roxy_tag_importMethodsFrom)
+S3method(roxy_tag_ns,roxy_tag_rawNamespace)
+S3method(roxy_tag_ns,roxy_tag_useDynLib)
 S3method(roxy_tag_parse,default)
 S3method(roxy_tag_parse,roxy_tag_aliases)
 S3method(roxy_tag_parse,roxy_tag_author)
@@ -130,6 +144,38 @@ S3method(roxy_tag_parse,roxy_tag_templateVar)
 S3method(roxy_tag_parse,roxy_tag_title)
 S3method(roxy_tag_parse,roxy_tag_usage)
 S3method(roxy_tag_parse,roxy_tag_useDynLib)
+S3method(roxy_tag_rd,roxy_tag_.formals)
+S3method(roxy_tag_rd,roxy_tag_.methods)
+S3method(roxy_tag_rd,roxy_tag_.reexport)
+S3method(roxy_tag_rd,roxy_tag_author)
+S3method(roxy_tag_rd,roxy_tag_backref)
+S3method(roxy_tag_rd,roxy_tag_concept)
+S3method(roxy_tag_rd,roxy_tag_description)
+S3method(roxy_tag_rd,roxy_tag_details)
+S3method(roxy_tag_rd,roxy_tag_docType)
+S3method(roxy_tag_rd,roxy_tag_encoding)
+S3method(roxy_tag_rd,roxy_tag_evalRd)
+S3method(roxy_tag_rd,roxy_tag_example)
+S3method(roxy_tag_rd,roxy_tag_examples)
+S3method(roxy_tag_rd,roxy_tag_family)
+S3method(roxy_tag_rd,roxy_tag_field)
+S3method(roxy_tag_rd,roxy_tag_format)
+S3method(roxy_tag_rd,roxy_tag_includeRmd)
+S3method(roxy_tag_rd,roxy_tag_inherit)
+S3method(roxy_tag_rd,roxy_tag_inheritDotParams)
+S3method(roxy_tag_rd,roxy_tag_inheritParams)
+S3method(roxy_tag_rd,roxy_tag_inheritSection)
+S3method(roxy_tag_rd,roxy_tag_keywords)
+S3method(roxy_tag_rd,roxy_tag_note)
+S3method(roxy_tag_rd,roxy_tag_param)
+S3method(roxy_tag_rd,roxy_tag_rawRd)
+S3method(roxy_tag_rd,roxy_tag_references)
+S3method(roxy_tag_rd,roxy_tag_return)
+S3method(roxy_tag_rd,roxy_tag_section)
+S3method(roxy_tag_rd,roxy_tag_seealso)
+S3method(roxy_tag_rd,roxy_tag_slot)
+S3method(roxy_tag_rd,roxy_tag_source)
+S3method(roxy_tag_rd,roxy_tag_title)
 export(block_get_tag)
 export(block_get_tag_value)
 export(block_get_tags)
diff --git a/NEWS.md b/NEWS.md
index fd5238df4..b9b1ba40e 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -1,5 +1,9 @@
 # roxygen2 (development version)
 
+* Using `@docType package` no longer automatically adds `-name`. Instead 
+  document `_PACKAGE` to get all the defaults for package documentation.
+  (Removing this feature allowed for simplification of the code.)
+
 * `roclet_tags()` is no longer used; instead define a `roxy_tag_parse()` method.
   If you tag is `@mytag`, then the class name associated with it is
   `roxy_tag_mytag` so the method should be call `roxy_tag_parse.roxy_tag_mytag()`.
diff --git a/R/namespace.R b/R/namespace.R
index a3570afed..11b71446f 100644
--- a/R/namespace.R
+++ b/R/namespace.R
@@ -1,22 +1,3 @@
-# Processed first
-ns_tags_import <- c(
-  "import",
-  "importFrom",
-  "importClassesFrom",
-  "importMethodsFrom",
-  "useDynLib",
-  "rawNamespace"
-)
-ns_tags <- c(
-  ns_tags_import,
-  "evalNamespace",
-  "export",
-  "exportClass",
-  "exportMethod",
-  "exportS3Method",
-  "exportPattern"
-)
-
 #' Roclet: make `NAMESPACE`
 #'
 #' @description
@@ -55,7 +36,7 @@ roclet_preprocess.roclet_namespace <- function(x,
                                                base_path,
                                                global_options = list()) {
 
-  lines <- blocks_to_ns(blocks, env, tag_set = ns_tags_import)
+  lines <- blocks_to_ns(blocks, env, import_only = TRUE)
   NAMESPACE <- file.path(base_path, "NAMESPACE")
 
   if (length(lines) == 0 && !made_by_roxygen(NAMESPACE)) {
@@ -63,7 +44,7 @@ roclet_preprocess.roclet_namespace <- function(x,
   }
 
   results <- c(made_by("#"), lines)
-  write_if_different(NAMESPACE, results, check = FALSE)
+  write_if_different(NAMESPACE, results, check = TRUE)
 
   invisible(x)
 }
@@ -77,30 +58,6 @@ roclet_process.roclet_namespace <- function(x,
   blocks_to_ns(blocks, env)
 }
 
-#' @export
-roxy_tag_parse.roxy_tag_evalNamespace <- function(x) tag_code(x)
-#' @export
-roxy_tag_parse.roxy_tag_export <- function(x) tag_words_line(x)
-#' @export
-roxy_tag_parse.roxy_tag_exportClass <- function(x) tag_words(x, 1)
-#' @export
-roxy_tag_parse.roxy_tag_exportS3Method <- function(x) tag_words(x, min = 0, max = 2)
-#' @export
-roxy_tag_parse.roxy_tag_exportMethod <- function(x) tag_words(x, min = 1)
-#' @export
-roxy_tag_parse.roxy_tag_exportPattern <- function(x) tag_words(x, min = 1)
-#' @export
-roxy_tag_parse.roxy_tag_import <- function(x) tag_words(x, min = 1)
-#' @export
-roxy_tag_parse.roxy_tag_importClassesFrom <- function(x) tag_words(x, min = 2)
-#' @export
-roxy_tag_parse.roxy_tag_importFrom <- function(x) tag_words(x, min = 2)
-#' @export
-roxy_tag_parse.roxy_tag_importMethodsFrom <- function(x) tag_words(x, min = 2)
-#' @export
-roxy_tag_parse.roxy_tag_rawNamespace <- function(x) tag_code(x)
-#' @export
-roxy_tag_parse.roxy_tag_useDynLib <- function(x) tag_words(x, min = 1)
 
 #' @export
 roclet_output.roclet_namespace <- function(x, results, base_path, ...) {
@@ -124,86 +81,185 @@ roclet_clean.roclet_namespace <- function(x, base_path) {
 
 # NAMESPACE generation ----------------------------------------------------
 
-blocks_to_ns <- function(blocks, env, tag_set = ns_tags) {
-  lines <- map(blocks, block_to_ns, env = env, tag_set = tag_set)
+blocks_to_ns <- function(blocks, env, import_only = FALSE) {
+  lines <- map(blocks, block_to_ns, env = env, import_only = import_only)
   lines <- unlist(lines) %||% character()
 
   sort_c(unique(lines))
 }
 
-block_to_ns <- function(block, env, tag_set = ns_tags) {
-  tags <- block_get_tags(block, tag_set)
+block_to_ns <- function(block, env, import_only = FALSE) {
+  map(block$tags, roxy_tag_ns, block = block, env = env, import_only = import_only)
+}
+
+# Namespace tag methods ---------------------------------------------------
 
-  map(tags, function(tag) {
-    exec(paste0("ns_", tag$tag), tag, block, env)
-  })
+roxy_tag_ns <- function(x, block, env, import_only = FALSE) {
+  UseMethod("roxy_tag_ns")
 }
 
-ns_export <- function(tag, block, env) {
-  if (identical(tag$val, "")) {
+#' @export
+roxy_tag_ns.default <- function(x, block, env, import_only = FALSE) {
+
+}
+
+#' @export
+roxy_tag_parse.roxy_tag_evalNamespace <- function(x) {
+  tag_code(x)
+}
+#' @export
+roxy_tag_ns.roxy_tag_evalNamespace <- function(x, block, env, import_only = FALSE) {
+  roxy_tag_eval(x, env)
+}
+
+#' @export
+roxy_tag_parse.roxy_tag_export <- function(x) {
+  tag_words_line(x)
+}
+#' @export
+roxy_tag_ns.roxy_tag_export <- function(x, block, env, import_only = FALSE) {
+  if (import_only) {
+    return()
+  }
+
+  if (identical(x$val, "")) {
     # FIXME: check for empty exports (i.e. no name)
     default_export(block$object, block)
   } else {
-    export(tag$val)
+    export(x$val)
   }
 }
 
-ns_exportClass       <- function(tag, block, env) export_class(tag$val)
-ns_exportMethod      <- function(tag, block, env) export_s4_method(tag$val)
-ns_exportPattern     <- function(tag, block, env) one_per_line("exportPattern", tag$val)
-ns_import            <- function(tag, block, env) one_per_line("import", tag$val)
-ns_importFrom        <- function(tag, block, env) repeat_first("importFrom", tag$val)
-ns_importClassesFrom <- function(tag, block, env) repeat_first("importClassesFrom", tag$val)
-ns_importMethodsFrom <- function(tag, block, env) repeat_first("importMethodsFrom", tag$val)
+#' @export
+roxy_tag_parse.roxy_tag_exportClass <- function(x) {
+  tag_words(x, 1)
+}
+#' @export
+roxy_tag_ns.roxy_tag_exportClass <- function(x, block, env, import_only = FALSE) {
+  if (import_only) {
+    return()
+  }
 
-ns_exportS3Method    <- function(tag, block, env) {
-  obj <- block$object
+  export_class(x$val)
+}
+
+#' @export
+roxy_tag_parse.roxy_tag_exportMethod <- function(x) {
+  tag_words(x, min = 1)
+}
+#' @export
+roxy_tag_ns.roxy_tag_exportMethod <- function(x, block, env, import_only = FALSE) {
+  if (import_only) {
+    return()
+  }
+  export_s4_method(x$val)
+}
 
-  if (length(tag$val) < 2 && !inherits(obj, "s3method")) {
-    roxy_tag_warning(tag,
+#' @export
+roxy_tag_parse.roxy_tag_exportPattern <- function(x) {
+  tag_words(x, min = 1)
+}
+#' @export
+roxy_tag_ns.roxy_tag_exportPattern <- function(x, block, env, import_only = FALSE) {
+  if (import_only) {
+    return()
+  }
+  one_per_line("exportPattern", x$val)
+}
+
+#' @export
+roxy_tag_parse.roxy_tag_exportS3Method <- function(x) {
+  tag_words(x, min = 0, max = 2)
+}
+#' @export
+roxy_tag_ns.roxy_tag_exportS3Method <- function(x, block, env, import_only = FALSE) {
+  if (import_only) {
+    return()
+  }
+
+  obj <- block$object
+  if (length(x$val) < 2 && !inherits(obj, "s3method")) {
+    roxy_tag_warning(x,
       "`@exportS3Method` and `@exportS3Method generic` must be used with an S3 method"
     )
     return()
   }
 
-  if (identical(tag$val, "")) {
+  if (identical(x$val, "")) {
     method <- attr(obj$value, "s3method")
-  } else if (length(tag$val) == 1) {
-    method <- c(tag$val, attr(obj$value, "s3method")[[2]])
+  } else if (length(x$val) == 1) {
+    method <- c(x$val, attr(obj$value, "s3method")[[2]])
   } else {
-    method <- tag$val
+    method <- x$val
   }
 
   export_s3_method(method)
 }
 
-ns_useDynLib         <- function(tag, block, env) {
-  if (length(tag$val) == 1) {
-    return(paste0("useDynLib(", auto_quote(tag$val), ")"))
+#' @export
+roxy_tag_parse.roxy_tag_import <- function(x) {
+  tag_words(x, min = 1)
+}
+#' @export
+roxy_tag_ns.roxy_tag_import <- function(x, block, env, import_only = FALSE) {
+  one_per_line("import", x$val)
+}
+
+#' @export
+roxy_tag_parse.roxy_tag_importClassesFrom <- function(x) {
+  tag_words(x, min = 2)
+}
+#' @export
+roxy_tag_ns.roxy_tag_importClassesFrom <- function(x, block, env, import_only = FALSE) {
+  repeat_first("importClassesFrom", x$val)
+}
+
+#' @export
+roxy_tag_parse.roxy_tag_importFrom <- function(x) {
+  tag_words(x, min = 2)
+}
+#' @export
+roxy_tag_ns.roxy_tag_importFrom <- function(x, block, env, import_only = FALSE) {
+  repeat_first("importFrom", x$val)
+}
+
+#' @export
+roxy_tag_parse.roxy_tag_importMethodsFrom <- function(x) {
+  tag_words(x, min = 2)
+}
+#' @export
+roxy_tag_ns.roxy_tag_importMethodsFrom <- function(x, block, env, import_only = FALSE) {
+  repeat_first("importMethodsFrom", x$val)
+}
+
+#' @export
+roxy_tag_parse.roxy_tag_rawNamespace <- function(x) {
+  tag_code(x)
+}
+#' @export
+roxy_tag_ns.roxy_tag_rawNamespace  <- function(x, block, env, import_only = FALSE) {
+  x$val
+}
+
+#' @export
+roxy_tag_parse.roxy_tag_useDynLib <- function(x) {
+  tag_words(x, min = 1)
+}
+#' @export
+roxy_tag_ns.roxy_tag_useDynLib <- function(x, block, env, import_only = FALSE) {
+  if (length(x$val) == 1) {
+    return(paste0("useDynLib(", auto_quote(x$val), ")"))
   }
 
-  if (any(grepl(",", tag$val))) {
+  if (any(grepl(",", x$val))) {
     # If there's a comma in list, don't quote output. This makes it possible
     # for roxygen2 to support other NAMESPACE forms not otherwise mapped
-    args <- paste0(tag$val, collapse = " ")
+    args <- paste0(x$val, collapse = " ")
     paste0("useDynLib(", args, ")")
   } else {
-    repeat_first("useDynLib", tag$val)
+    repeat_first("useDynLib", x$val)
   }
 }
-ns_rawNamespace  <- function(tag, block, env) tag$val
-ns_evalNamespace <- function(tag, block, env) {
-  roxy_tag_eval(tag, env)
-}
-
-# Functions used by both default_export and ns_* functions
-export           <- function(x) one_per_line("export", x)
-export_class     <- function(x) one_per_line("exportClasses", x)
-export_s4_method <- function(x) one_per_line("exportMethods", x)
-export_s3_method <- function(x) {
-  args <- paste0(auto_backtick(x), collapse = ",")
-  paste0("S3method(", args, ")")
-}
 
 # Default export methods --------------------------------------------------
 
@@ -223,9 +279,16 @@ default_export.default   <- function(x, block) export(x$alias)
 #' @export
 default_export.NULL      <- function(x, block) export(block_get_tag_value(block, "name"))
 
-
 # Helpers -----------------------------------------------------------------
 
+export           <- function(x) one_per_line("export", x)
+export_class     <- function(x) one_per_line("exportClasses", x)
+export_s4_method <- function(x) one_per_line("exportMethods", x)
+export_s3_method <- function(x) {
+  args <- paste0(auto_backtick(x), collapse = ",")
+  paste0("S3method(", args, ")")
+}
+
 one_per_line <- function(name, x) {
   paste0(name, "(", auto_backtick(x), ")")
 }
diff --git a/R/object-defaults.R b/R/object-defaults.R
index 5df30bd6c..3518e3f58 100644
--- a/R/object-defaults.R
+++ b/R/object-defaults.R
@@ -3,6 +3,14 @@ object_defaults <- function(x) UseMethod("object_defaults")
 #' @export
 object_defaults.default <- function(x) list()
 
+#' @exportS3Method object_defaults "function"
+object_defaults.function <- function(x) {
+  # Used in process_inherit_params()
+  list(
+    roxy_tag(".formals", NULL, names(formals(x$value)))
+  )
+}
+
 #' @export
 object_defaults.data <- function(x) {
   str_out <- rd(object_format(x$value))
@@ -44,7 +52,7 @@ object_defaults.import <- function(x) {
     roxy_tag("name", NULL, "reexports"),
     roxy_tag("keywords", NULL, "internal"),
     roxy_tag("title", NULL, "Objects exported from other packages"),
-    roxy_tag(".reexport", NULL, roxy_field_reexport(x$value$pkg, x$value$fun))
+    roxy_tag(".reexport", NULL, list(pkg = x$value$pkg, fun = x$value$fun))
   )
 }
 
@@ -58,7 +66,9 @@ object_defaults.s4class <- function(x) {
 #' @export
 object_defaults.rcclass <- function(x) {
   list(
-    roxy_tag("docType", NULL, "class")
+    roxy_tag("docType", NULL, "class"),
+    if (!is.null(x$methods))
+      roxy_tag(".methods", NULL, x$methods)
   )
 }
 
@@ -68,3 +78,10 @@ object_defaults.s4method <- function(x) {
     roxy_tag("docType", NULL, "class")
   )
 }
+
+# Helpers -----------------------------------------------------------------
+
+package_suffix <- function(name) {
+  paste0(name, "-package")
+}
+
diff --git a/R/rd-examples.R b/R/rd-examples.R
index c25fe83b0..264d0d388 100644
--- a/R/rd-examples.R
+++ b/R/rd-examples.R
@@ -1,32 +1,34 @@
-topic_add_examples <- function(topic, block, base_path) {
-  tags <- block_get_tags(block, c("examples", "example"))
+#' @export
+roxy_tag_parse.roxy_tag_example <- function(x) {
+  x <- tag_value(x)
 
-  for (tag in tags) {
-    if (tag$tag == "examples") {
-      example <- tag$val
-    } else {
-      example <- read_example_from_path(tag, base_path)
-    }
-    topic$add_simple_field("examples", example)
-  }
-}
-
-read_example_from_path <- function(tag, base_path) {
-  path <- str_trim(tag$val)
-  nl <- str_count(path, "\n")
+  nl <- str_count(x$val, "\n")
   if (any(nl) > 0) {
-    roxy_tag_warning(tag, "spans multiple lines. Do you want @examples?")
+    roxy_tag_warning(x, "spans multiple lines. Do you want @examples?")
     return()
   }
 
-  path <- file.path(base_path, path)
+  x
+}
+#' @export
+roxy_tag_rd.roxy_tag_example <- function(x, topic, base_path, env) {
+  path <- file.path(base_path, x$val)
   if (!file.exists(path)) {
-    roxy_tag_warning(tag, "'", path, "' doesn't exist")
+    roxy_tag_warning(x, "'", path, "' doesn't exist")
     return()
   }
 
   code <- read_lines(path)
-  escape_examples(code)
+  topic$add_simple_field("examples", escape_examples(code))
+}
+
+#' @export
+roxy_tag_parse.roxy_tag_examples <- function(x) {
+  tag_examples(x)
+}
+#' @export
+roxy_tag_rd.roxy_tag_examples <- function(x, topic, base_path, env) {
+  topic$add_simple_field("examples", x$val)
 }
 
 # Works like escape, but unescapes special rd example commands.
diff --git a/R/rd-include-rmd.R b/R/rd-include-rmd.R
index 1b7911a9a..55a3ee089 100644
--- a/R/rd-include-rmd.R
+++ b/R/rd-include-rmd.R
@@ -1,5 +1,4 @@
-
-block_include_rmd <- function(tag, block, base_path) {
+roxy_tag_include_rmd <- function(tag, base_path) {
   rmd <- tag$val
   stopifnot(is.character(rmd), length(rmd) == 1, !is.na(rmd))
 
diff --git a/R/rd-inherit.R b/R/rd-inherit.R
index 9fb11fef7..969c122d0 100644
--- a/R/rd-inherit.R
+++ b/R/rd-inherit.R
@@ -1,3 +1,39 @@
+#' @export
+roxy_tag_parse.roxy_tag_inherit <- function(x) tag_inherit(x)
+#' @export
+roxy_tag_rd.roxy_tag_inherit <- function(x, topic, base_path, env) {
+  field <- roxy_field_inherit(x$val$source, list(x$val$fields))
+  topic$add_field(field)
+}
+
+#' @export
+roxy_tag_parse.roxy_tag_inheritParams <- function(x) tag_value(x)
+#' @export
+roxy_tag_rd.roxy_tag_inheritParams <- function(x, topic, base_path, env) {
+  field <- roxy_field_inherit(x$val, list("params"))
+  topic$add_field(field)
+}
+
+#' @export
+roxy_tag_parse.roxy_tag_inheritDotParams <- function(x) {
+  tag_two_part(x, "source", "args", required = FALSE, markdown = FALSE)
+}
+#' @export
+roxy_tag_rd.roxy_tag_inheritDotParams <- function(x, topic, base_path, env) {
+  field <- roxy_field_inherit_dot_params(x$val$source, x$val$args)
+  topic$add_field(field)
+}
+
+#' @export
+roxy_tag_parse.roxy_tag_inheritSection <- function(x) tag_name_description(x)
+#' @export
+roxy_tag_rd.roxy_tag_inheritSection <- function(x, topic, base_path, env) {
+  field <- roxy_field_inherit_section(x$val$name, x$val$description)
+  topic$add_field(field)
+}
+
+# Process inheritance -----------------------------------------------------
+
 topics_process_inherit <- function(topics, env) {
   inherits <- function(type) {
     function(x) x$inherits_from(type)
diff --git a/R/rd.R b/R/rd.R
index 4e6010bda..c8c04cbd6 100644
--- a/R/rd.R
+++ b/R/rd.R
@@ -118,32 +118,13 @@ block_to_rd <- function(block, base_path, env, global_options = list()) {
     return()
   }
 
-  # Note that order of operations here doesn't matter: fields are
-  # ordered by RoxyFile$format()
   rd <- RoxyTopic$new()
   topic_add_name_aliases(rd, block, name)
-
-  # Some fields added directly by roxygen internals
-  tags <- Filter(roxy_tag_is_field, block$tags)
-  for (tag in tags) {
-    rd$add(tag$val)
+  for (tag in block$tags) {
+    roxy_tag_rd(tag, rd, env = env, base_path = base_path)
   }
 
-  topic_add_backref(rd, block)
-  topic_add_doc_type(rd, block)
-  topic_add_eval_rd(rd, block, env)
-  topic_add_include_rmd(rd, block, base_path)
-  topic_add_examples(rd, block, base_path)
-  topic_add_fields(rd, block)
-  topic_add_inherit(rd, block)
-  topic_add_keyword(rd, block)
-  topic_add_methods(rd, block)
-  topic_add_params(rd, block)
-  topic_add_simple_tags(rd, block)
-  topic_add_sections(rd, block)
-  topic_add_slots(rd, block)
   topic_add_usage(rd, block, old_usage = global_options$old_usage)
-  topic_add_value(rd, block)
 
   if (rd$has_field("description") && rd$has_field("reexport")) {
     roxy_tag_warning(block$tags[[1]], "Can't use description when re-exporting")
@@ -157,87 +138,7 @@ block_to_rd <- function(block, base_path, env, global_options = list()) {
   rd
 }
 
-#' @export
-roxy_tag_parse.roxy_tag_aliases <- function(x) tag_value(x)
-#' @export
-roxy_tag_parse.roxy_tag_author <- function(x) tag_markdown(x)
-#' @export
-roxy_tag_parse.roxy_tag_backref <- function(x) tag_value(x)
-#' @export
-roxy_tag_parse.roxy_tag_concept <- function(x) tag_markdown(x)
-#' @export
-roxy_tag_parse.roxy_tag_describeIn <- function(x) tag_name_description(x)
-#' @export
-roxy_tag_parse.roxy_tag_description <- function(x) tag_markdown_with_sections(x)
-#' @export
-roxy_tag_parse.roxy_tag_details <- function(x) tag_markdown_with_sections(x)
-#' @export
-roxy_tag_parse.roxy_tag_docType <- function(x) tag_name(x)
-#' @export
-roxy_tag_parse.roxy_tag_encoding <- function(x) tag_value(x)
-#' @export
-roxy_tag_parse.roxy_tag_evalRd <- function(x) tag_code(x)
-#' @export
-roxy_tag_parse.roxy_tag_example <- function(x) tag_value(x)
-#' @export
-roxy_tag_parse.roxy_tag_examples <- function(x) tag_examples(x)
-#' @export
-roxy_tag_parse.roxy_tag_family <- function(x) tag_value(x)
-#' @export
-roxy_tag_parse.roxy_tag_field <- function(x) tag_name_description(x)
-#' @export
-roxy_tag_parse.roxy_tag_format <- function(x) tag_markdown(x)
-#' @export
-roxy_tag_parse.roxy_tag_includeRmd <- function(x) tag_value(x)
-#' @export
-roxy_tag_parse.roxy_tag_inherit <- function(x) tag_inherit(x)
-#' @export
-roxy_tag_parse.roxy_tag_inheritParams <- function(x) tag_value(x)
-#' @export
-roxy_tag_parse.roxy_tag_inheritDotParams <- function(x) tag_two_part(x, "source", "args", required = FALSE, markdown = FALSE)
-#' @export
-roxy_tag_parse.roxy_tag_inheritSection <- function(x) tag_name_description(x)
-#' @export
-roxy_tag_parse.roxy_tag_keywords <- function(x) tag_value(x)
-#' @export
-roxy_tag_parse.roxy_tag_method <- function(x) tag_words(x, 2, 2)
-#' @export
-roxy_tag_parse.roxy_tag_name <- function(x) tag_value(x)
-#' @export
-roxy_tag_parse.roxy_tag_md <- function(x) tag_toggle(x)
-#' @export
-roxy_tag_parse.roxy_tag_noMd <- function(x) tag_toggle(x)
-#' @export
-roxy_tag_parse.roxy_tag_noRd <- function(x) tag_toggle(x)
-#' @export
-roxy_tag_parse.roxy_tag_note <- function(x) tag_markdown(x)
-#' @export
-roxy_tag_parse.roxy_tag_param <- function(x) tag_name_description(x)
-#' @export
-roxy_tag_parse.roxy_tag_rdname <- function(x) tag_value(x)
-#' @export
-roxy_tag_parse.roxy_tag_rawRd <- function(x) tag_value(x)
-#' @export
-roxy_tag_parse.roxy_tag_references <- function(x) tag_markdown(x)
-#' @export
-roxy_tag_parse.roxy_tag_return <- function(x) tag_markdown(x)
-#' @export
-roxy_tag_parse.roxy_tag_section <- function(x) tag_markdown(x)
-#' @export
-roxy_tag_parse.roxy_tag_seealso <- function(x) tag_markdown(x)
-#' @export
-roxy_tag_parse.roxy_tag_slot <- function(x) tag_name_description(x)
-#' @export
-roxy_tag_parse.roxy_tag_source <- function(x) tag_markdown(x)
-#' @export
-roxy_tag_parse.roxy_tag_template <- function(x) tag_value(x)
-#' @export
-roxy_tag_parse.roxy_tag_templateVar <- function(x) tag_name_description(x)
-#' @export
-roxy_tag_parse.roxy_tag_title <- function(x) tag_markdown(x)
-#' @export
-roxy_tag_parse.roxy_tag_usage <- function(x) tag_value(x)
-
+# Special cases -----------------------------------------------------------
 
 topics_add_default_description <- function(topics) {
   for (topic in topics$topics) {
@@ -253,45 +154,6 @@ topics_add_default_description <- function(topics) {
   invisible()
 }
 
-
-topic_add_backref <- function(topic, block) {
-  tags <- block_get_tags(block, "backref")
-  for (tag in tags) {
-    topic$add_simple_field("backref", tag$val)
-  }
-}
-
-# Simple tags can be converted directly to fields
-topic_add_simple_tags <- function(topic, block) {
-  simple_tags <- block_get_tags(block,
-    c(
-      "author", "concept", "description", "details", "encoding", "family",
-      "format", "note", "rawRd", "references",
-      "seealso", "source", "title"
-    )
-  )
-
-  for (tag in simple_tags) {
-    if (length(tag$val) && nchar(tag$val[[1]])) {
-      topic$add_simple_field(tag$tag, tag$val[[1]])
-    }
-    for (extra in tag$val[-1]) {
-      topic$add_simple_field("rawRd", extra)
-    }
-  }
-}
-
-topic_add_params <- function(topic, block) {
-  # Used in process_inherit_params()
-  value <- block$object$value
-  if (is.function(value)) {
-    formals <- formals(value)
-    topic$add_simple_field("formals", names(formals))
-  }
-
-  process_def_tag(topic, block, "param")
-}
-
 topic_add_name_aliases <- function(topic, block, name) {
   tags <- block_get_tags(block, "aliases")
 
@@ -314,16 +176,48 @@ topic_add_name_aliases <- function(topic, block, name) {
   topic$add_simple_field("alias", aliases)
 }
 
+# Prefer explicit \code{@@usage} to a \code{@@formals} list.
+topic_add_usage <- function(topic, block, old_usage = FALSE) {
+  tag <- block_get_tag(block, "usage")
 
-topic_add_methods <- function(topic, block) {
-  obj <- block$object
-  if (!inherits(obj, "rcclass")) return()
+  if (is.null(tag)) {
+    usage <- object_usage(block$object, old_usage = old_usage)
+  } else if (tag$val == "NULL") {
+    usage <- NULL
+  } else {
+    # Treat user input as already escaped, otherwise they have no way
+    # to enter \S4method etc.
+    usage <- rd(tag$val)
+  }
+  topic$add_simple_field("usage", usage)
+}
+
+# Tag-wise processing -----------------------------------------------------
+
+roxy_tag_rd <- function(x, topic, base_path, env) {
+  UseMethod("roxy_tag_rd")
+}
+
+roxy_tag_rd.default <- function(x, topic, base_path, env) {
+}
+
+
+# Internal tags -----------------------------------------------------------
+
+#' @export
+roxy_tag_rd.roxy_tag_.reexport <- function(x, topic, base_path, env) {
+  topic$add(roxy_field_reexport(x$val$pkg, x$val$fun))
+}
 
-  methods <- obj$methods
-  if (is.null(obj$methods)) return()
+#' @export
+roxy_tag_rd.roxy_tag_.formals <- function(x, topic, base_path, env) {
+  topic$add_simple_field("formals", x$val)
+}
 
-  desc <- lapply(methods, function(x) docstring(x$value@.Data))
-  usage <- map_chr(methods, function(x) {
+#' @export
+roxy_tag_rd.roxy_tag_.methods <- function(x, topic, base_path, env) {
+  desc <- lapply(x$val, function(x) docstring(x$value@.Data))
+  usage <- map_chr(x$val, function(x) {
     function_usage(x$value@name, formals(x$value@.Data))
   })
 
@@ -334,148 +228,216 @@ topic_add_methods <- function(topic, block) {
   topic$add_simple_field("rcmethods", setNames(desc, usage))
 }
 
-topic_add_inherit <- function(topic, block) {
-  tags <- block_get_tags(block, "inherit")
-  for (tag in tags) {
-    field <- roxy_field_inherit(tag$val$source, list(tag$val$fields))
-    topic$add_field(field)
-  }
+# Regular tags ------------------------------------------------------------
 
-  tags <- block_get_tags(block, "inheritParams")
-  for (tag in tags) {
-    field <- roxy_field_inherit(tag$val, list("params"))
-    topic$add_field(field)
-  }
+#' @export
+roxy_tag_parse.roxy_tag_aliases <- function(x) tag_value(x)
 
-  tags <- block_get_tags(block, "inheritSection")
-  for (tag in tags) {
-    field <- roxy_field_inherit_section(tag$val$name, tag$val$description)
-    topic$add_field(field)
-  }
+#' @export
+roxy_tag_parse.roxy_tag_author <- function(x) tag_markdown(x)
+#' @export
+roxy_tag_rd.roxy_tag_author <- function(x, topic, base_path, env) {
+  topic$add_markdown_field(x$tag, x$val)
+}
 
-  tags <- block_get_tags(block, "inheritDotParams")
-  for (tag in tags) {
-    field <- roxy_field_inherit_dot_params(tag$val$source, tag$val$args)
-    topic$add_field(field)
-  }
+#' @export
+roxy_tag_parse.roxy_tag_backref <- function(x) tag_value(x)
+#' @export
+roxy_tag_rd.roxy_tag_backref <- function(x, topic, base_path, env) {
+  topic$add_simple_field("backref", x$val)
 }
 
+#' @export
+roxy_tag_parse.roxy_tag_concept <- function(x) tag_markdown(x)
+#' @export
+roxy_tag_rd.roxy_tag_concept <- function(x, topic, base_path, env) {
+  topic$add_markdown_field(x$tag, x$val)
+}
 
-topic_add_value <- function(topic, block) {
-  tags <- block_get_tags(block, "return")
+#' @export
+roxy_tag_parse.roxy_tag_describeIn <- function(x) tag_name_description(x)
 
-  for (tag in tags) {
-    topic$add_simple_field("value", tag$val)
-  }
+#' @export
+roxy_tag_parse.roxy_tag_description <- function(x) tag_markdown_with_sections(x)
+#' @export
+roxy_tag_rd.roxy_tag_description <- function(x, topic, base_path, env) {
+  topic$add_markdown_field(x$tag, x$val)
 }
 
-topic_add_keyword <- function(topic, block) {
-  tags <- block_get_tags(block, "keywords")
-
-  vals <- map_chr(tags, "val")
-  keywords <- unlist(str_split(vals, "\\s+"))
+#' @export
+roxy_tag_parse.roxy_tag_details <- function(x) tag_markdown_with_sections(x)
+#' @export
+roxy_tag_rd.roxy_tag_details <- function(x, topic, base_path, env) {
+  topic$add_markdown_field(x$tag, x$val)
+}
 
-  topic$add_simple_field("keyword", keywords)
+#' @export
+roxy_tag_parse.roxy_tag_docType <- function(x) tag_name(x)
+#' @export
+roxy_tag_rd.roxy_tag_docType <- function(x, topic, base_path, env) {
+  topic$add_simple_field("docType", x$val)
 }
 
-# Prefer explicit \code{@@usage} to a \code{@@formals} list.
-topic_add_usage <- function(topic, block, old_usage = FALSE) {
-  tag <- block_get_tag(block, "usage")
+#' @export
+roxy_tag_parse.roxy_tag_encoding <- function(x) tag_value(x)
+#' @export
+roxy_tag_rd.roxy_tag_encoding <- function(x, topic, base_path, env) {
+  topic$add_markdown_field(x$tag, x$val)
+}
 
-  if (is.null(tag)) {
-    usage <- object_usage(block$object, old_usage = old_usage)
-  } else if (tag$val == "NULL") {
-    usage <- NULL
-  } else {
-    # Treat user input as already escaped, otherwise they have no way
-    # to enter \S4method etc.
-    usage <- rd(tag$val)
-  }
-  topic$add_simple_field("usage", usage)
+#' @export
+roxy_tag_parse.roxy_tag_evalRd <- function(x) tag_code(x)
+#' @export
+roxy_tag_rd.roxy_tag_evalRd <- function(x, topic, base_path, env) {
+  topic$add_simple_field("rawRd", roxy_tag_eval(x, env))
 }
 
-topic_add_slots <- function(topic, block) {
-  process_def_tag(topic, block, "slot")
+#' @export
+roxy_tag_parse.roxy_tag_family <- function(x) tag_value(x)
+#' @export
+roxy_tag_rd.roxy_tag_family <- function(x, topic, base_path, env) {
+  topic$add_markdown_field(x$tag, x$val)
 }
 
-topic_add_fields <- function(topic, block) {
-  process_def_tag(topic, block, "field")
+#' @export
+roxy_tag_parse.roxy_tag_field <- function(x) tag_name_description(x)
+#' @export
+roxy_tag_rd.roxy_tag_field <- function(x, topic, base_path, env) {
+  value <- setNames(x$val$description, x$val$name)
+  topic$add_simple_field(x$tag, value)
 }
 
-topic_add_eval_rd <- function(topic, block, env) {
-  tags <- block_get_tags(block, "evalRd")
+#' @export
+roxy_tag_parse.roxy_tag_format <- function(x) tag_markdown(x)
+#' @export
+roxy_tag_rd.roxy_tag_format <- function(x, topic, base_path, env) {
+  topic$add_markdown_field(x$tag, x$val)
+}
 
-  for (tag in tags) {
-    out <- roxy_tag_eval(tag, env)
-    topic$add_simple_field("rawRd", out)
+#' @export
+roxy_tag_parse.roxy_tag_includeRmd <- function(x) {
+  if (!is_installed("rmarkdown")) {
+    roxy_tag_warning(x, "Needs the rmarkdown package")
+    return()
   }
+
+  tag_value(x)
+}
+#' @export
+roxy_tag_rd.roxy_tag_includeRmd <- function(x, topic, base_path, env) {
+  topic$add_markdown_field("details", roxy_tag_include_rmd(x, base_path))
+}
+
+#' @export
+roxy_tag_parse.roxy_tag_keywords <- function(x) tag_value(x)
+#' @export
+roxy_tag_rd.roxy_tag_keywords <- function(x, topic, base_path, env) {
+  topic$add_simple_field("keyword", str_split(x$val, "\\s+")[[1]])
 }
 
-topic_add_include_rmd <- function(topic, block, base_path) {
-  tags <- block_get_tags(block, "includeRmd")
+#' @export
+roxy_tag_parse.roxy_tag_method <- function(x) tag_words(x, 2, 2)
 
-  for (tag in tags) {
-    if (!is_installed("rmarkdown")) {
-      roxy_tag_warning(tag, "Needs the rmarkdown package")
-    }
-    out <- block_include_rmd(tag, block, base_path)
-    if (!is.null(out[[1]])) {
-      topic$add_simple_field("details", out[[1]])
-    }
-    lapply(out[-1], function(s) {
-      topic$add_simple_field("rawRd", s)
-    })
-  }
+#' @export
+roxy_tag_parse.roxy_tag_name <- function(x) tag_value(x)
+
+#' @export
+roxy_tag_parse.roxy_tag_md <- function(x) tag_toggle(x)
+
+#' @export
+roxy_tag_parse.roxy_tag_noMd <- function(x) tag_toggle(x)
+
+#' @export
+roxy_tag_parse.roxy_tag_noRd <- function(x) tag_toggle(x)
+
+#' @export
+roxy_tag_parse.roxy_tag_note <- function(x) tag_markdown(x)
+#' @export
+roxy_tag_rd.roxy_tag_note <- function(x, topic, base_path, env) {
+  topic$add_markdown_field(x$tag, x$val)
 }
 
-topic_add_sections <- function(topic, block) {
-  tags <- block_get_tags(block, "section")
+#' @export
+roxy_tag_parse.roxy_tag_param <- function(x) tag_name_description(x)
+#' @export
+roxy_tag_rd.roxy_tag_param <- function(x, topic, base_path, env) {
+  value <- setNames(x$val$description, x$val$name)
+  topic$add_simple_field(x$tag, value)
+}
 
-  for (tag in tags) {
-    pieces <- str_split(tag$val, ":", n = 2)[[1]]
+#' @export
+roxy_tag_parse.roxy_tag_rdname <- function(x) tag_value(x)
 
-    title <- str_split(pieces[1], "\n")[[1]]
-    if (length(title) > 1) {
-      roxy_tag_warning(tag,
-        "Section title spans multiple lines: \n", "@section ", title[1]
-      )
-      return()
-    }
+#' @export
+roxy_tag_parse.roxy_tag_rawRd <- function(x) tag_value(x)
+#' @export
+roxy_tag_rd.roxy_tag_rawRd <- function(x, topic, base_path, env) {
+  topic$add_markdown_field(x$tag, x$val)
+}
 
-    topic$add_field(roxy_field_section(pieces[1], pieces[2]))
-  }
+#' @export
+roxy_tag_parse.roxy_tag_references <- function(x) tag_markdown(x)
+#' @export
+roxy_tag_rd.roxy_tag_references <- function(x, topic, base_path, env) {
+  topic$add_markdown_field(x$tag, x$val)
 }
 
-topic_add_doc_type <- function(topic, block) {
-  tag <- block_get_tag(block, "docType")
-  if (is.null(tag)) {
+#' @export
+roxy_tag_parse.roxy_tag_return <- function(x) tag_markdown(x)
+#' @export
+roxy_tag_rd.roxy_tag_return <- function(x, topic, base_path, env) {
+  topic$add_simple_field("value", x$val)
+}
+
+#' @export
+roxy_tag_parse.roxy_tag_section <- function(x) tag_markdown(x)
+#' @export
+roxy_tag_rd.roxy_tag_section <- function(x, topic, base_path, env) {
+  pieces <- str_split(x$val, ":", n = 2)[[1]]
+  title <- str_split(pieces[1], "\n")[[1]]
+
+  if (length(title) > 1) {
+    roxy_tag_warning(x, "Section title spans multiple lines")
     return()
   }
 
-  topic$add_simple_field("docType", tag$val)
+  topic$add_field(roxy_field_section(pieces[1], pieces[2]))
+}
 
-  if (tag$val == "package") {
-    name <- block_get_tag(block, "name")
-    if (!str_detect(name$val, "-package")) {
-      topic$add_simple_field("alias", package_suffix(name$val))
-    }
-  }
+#' @export
+roxy_tag_parse.roxy_tag_seealso <- function(x) tag_markdown(x)
+#' @export
+roxy_tag_rd.roxy_tag_seealso <- function(x, topic, base_path, env) {
+  topic$add_markdown_field(x$tag, x$val)
 }
 
-package_suffix <- function(name) {
-  paste0(name, "-package")
+#' @export
+roxy_tag_parse.roxy_tag_slot <- function(x) tag_name_description(x)
+#' @export
+roxy_tag_rd.roxy_tag_slot <- function(x, topic, base_path, env) {
+  value <- setNames(x$val$description, x$val$name)
+  topic$add_simple_field(x$tag, value)
 }
 
-# Name + description tags ------------------------------------------------------
+#' @export
+roxy_tag_parse.roxy_tag_source <- function(x) tag_markdown(x)
+#' @export
+roxy_tag_rd.roxy_tag_source <- function(x, topic, base_path, env) {
+  topic$add_markdown_field(x$tag, x$val)
+}
 
-process_def_tag <- function(topic, block, tag) {
-  tags <- block_get_tags(block, tag)
-  if (length(tags) == 0) {
-    return()
-  }
+#' @export
+roxy_tag_parse.roxy_tag_template <- function(x) tag_value(x)
 
-  desc <- str_trim(map_chr(tags, c("val", "description")))
-  names(desc) <- map_chr(tags, c("val", "name"))
+#' @export
+roxy_tag_parse.roxy_tag_templateVar <- function(x) tag_name_description(x)
 
-  topic$add_simple_field(tag, desc)
+#' @export
+roxy_tag_parse.roxy_tag_title <- function(x) tag_markdown(x)
+#' @export
+roxy_tag_rd.roxy_tag_title <- function(x, topic, base_path, env) {
+  topic$add_markdown_field(x$tag, x$val)
 }
+
+#' @export
+roxy_tag_parse.roxy_tag_usage <- function(x) tag_value(x)
diff --git a/R/tag.R b/R/tag.R
index 8cf7f56e1..ea71fbf5c 100644
--- a/R/tag.R
+++ b/R/tag.R
@@ -9,7 +9,8 @@
 #'
 #' @keywords internal
 #' @export
-#' @param tag Tag name
+#' @param tag Tag name. Arguments starting with `.` are reserved for internal
+#'   usage.
 #' @param raw Raw tag value, a string.
 #' @param val Parsed tag value, typically a character vector, but sometimes
 #'   a list. Usually filled in by `tag_parsers`
@@ -61,7 +62,7 @@ format.roxy_tag <- function(x, ..., file = NULL) {
       raw <- x$raw
     }
   } else {
-    raw <- "<generated by roxygen2>"
+    raw <- "<generated>"
   }
 
   parsed <- if (is.null(x$val)) "{unparsed}" else "{parsed}"
@@ -88,10 +89,6 @@ roxy_tag_warning <- function(x, ...) {
   NULL
 }
 
-roxy_tag_is_field <- function(tag) {
-  inherits(tag$val, "roxy_field")
-}
-
 roxy_tag_eval <- function(tag, env) {
   tryCatch({
     expr <- parse(text = tag$val)
diff --git a/R/topic.R b/R/topic.R
index f83430c76..5fa89718d 100644
--- a/R/topic.R
+++ b/R/topic.R
@@ -81,6 +81,16 @@ RoxyTopic <- R6::R6Class("RoxyTopic", public = list(
     invisible()
   },
 
+  add_markdown_field = function(name, values) {
+    if (length(values) && nchar(values[[1]])) {
+      self$add_simple_field(name, values[[1]])
+    }
+    # Any additional components are sections
+    for (extra in values[-1]) {
+      self$add_simple_field("rawRd", extra)
+    }
+  },
+
   add = function(x, overwrite = FALSE) {
     if (inherits(x, "RoxyTopic")) {
       self$add(x$fields, overwrite = overwrite)
diff --git a/man/roclet.Rd b/man/roclet.Rd
index 640adb725..c7eebbce4 100644
--- a/man/roclet.Rd
+++ b/man/roclet.Rd
@@ -26,13 +26,13 @@ roclet_tags(x)
 
 \item{blocks}{A list of \link{roxy_block} objects.}
 
+\item{results}{Value returned from your \code{roclet_process()} method.}
+
 \item{base_path}{Path to root of source package.}
 
 \item{global_options}{List of roxygen2 options.}
 
 \item{env}{Package environment.}
-
-\item{results}{Value returned from your \code{roclet_process()} method.}
 }
 \description{
 To create a new roclet, you will need to create a constructor function
diff --git a/man/roxy_tag.Rd b/man/roxy_tag.Rd
index f0e652a16..32ae1a8c1 100644
--- a/man/roxy_tag.Rd
+++ b/man/roxy_tag.Rd
@@ -13,7 +13,8 @@ roxy_tag_parse(x)
 roxy_tag_warning(x, ...)
 }
 \arguments{
-\item{tag}{Tag name}
+\item{tag}{Tag name. Arguments starting with \code{.} are reserved for internal
+usage.}
 
 \item{raw}{Raw tag value, a string.}
 
diff --git a/tests/testthat/test-block-print.txt b/tests/testthat/test-block-print.txt
index 1dcf3c3d1..7df455a7e 100644
--- a/tests/testthat/test-block-print.txt
+++ b/tests/testthat/test-block-print.txt
@@ -4,6 +4,7 @@
     [line:  1] @title 'This is a title' {parsed}
     [line:  5] @param 'x,y A number' {parsed}
     [line:  6] @export '' {parsed}
+    [????:???] @.formals '<generated>' {parsed}
     [????:???] @backref '<text>' {parsed}
   $call   f <- function(x, y) x + y
   $object <function> 
diff --git a/tests/testthat/test-rd-doctype.R b/tests/testthat/test-rd-doctype.R
index d271b6287..42224a88c 100644
--- a/tests/testthat/test-rd-doctype.R
+++ b/tests/testthat/test-rd-doctype.R
@@ -1,27 +1,5 @@
 context("Rd: docType")
 
-# Package --------------------------------------------------------------------
-
-test_that("@docType package automatically adds package alias when needed", {
-  out <- roc_proc_text(rd_roclet(), "
-    #' @name a
-    #' @title a
-    #' @docType package
-    NULL
-
-    #' @name a-package
-    #' @title a
-    #' @docType package
-    NULL")
-
-  alias_1 <- get_tag(out[[1]], "alias")$values
-  expect_equal(alias_1, c("a", "a-package"))
-
-  alias_2 <- get_tag(out[[2]], "alias")$values
-  expect_equal(alias_2, c("a-package"))
-})
-
-
 # Data --------------------------------------------------------------------
 
 test_that("@docType data automatically adds sensible defaults", {