From a840af8381ac44b9933bd170e3652a3b35b8e4f1 Mon Sep 17 00:00:00 2001
From: Nathaniel Catchpole <catch@35733.no-reply.drupal.org>
Date: Tue, 18 Feb 2014 10:54:10 +0000
Subject: [PATCH] Issue #2173655 by Cottser, xjm, jessebeach, alexpott,
 rlmumford: Refactor theme() to _theme(); make it a private API to discourage
 module developers from circumventing the renderable build system.

---
 core/includes/ajax.inc                        |  3 +-
 core/includes/common.inc                      | 35 ++++++++------
 core/includes/pager.inc                       | 28 +++++++----
 core/includes/theme.inc                       | 46 +++++++++----------
 .../Core/Extension/UpdateModuleHandler.php    |  2 +-
 core/lib/Drupal/Core/Theme/Registry.php       | 14 +++---
 .../lib/Drupal/Core/Utility/ThemeRegistry.php |  2 +-
 core/modules/block/block.api.php              |  2 +-
 core/modules/comment/comment.module           |  2 -
 core/modules/image/image.module               |  6 +--
 core/modules/node/node.api.php                |  2 +-
 .../Drupal/simpletest/DrupalUnitTestBase.php  |  4 +-
 .../Tests/DrupalUnitTestBaseTest.php          |  4 +-
 .../lib/Drupal/simpletest/WebTestBase.php     |  2 +-
 core/modules/system/entity.api.php            |  2 +-
 core/modules/system/form.api.php              |  6 ++-
 .../Drupal/system/Tests/Theme/TableTest.php   | 33 +++++++++++--
 .../Drupal/system/Tests/Theme/ThemeTest.php   | 16 +++----
 .../Tests/Theme/TwigDebugMarkupTest.php       |  8 ++--
 core/modules/system/system.api.php            | 18 ++++----
 .../EventSubscriber/ThemeTestSubscriber.php   |  7 ++-
 .../Drupal/theme_test/ThemeTestController.php |  6 +--
 .../modules/theme_test/theme_test.module      |  8 ++--
 .../TwigThemeTestController.php               |  2 +-
 core/modules/system/theme.api.php             | 37 ++++++++++++---
 core/modules/taxonomy/taxonomy.api.php        |  2 +-
 core/modules/update/update.manager.inc        |  4 +-
 core/modules/user/user.api.php                |  2 +-
 core/themes/engines/twig/twig.engine          |  4 +-
 29 files changed, 190 insertions(+), 117 deletions(-)

diff --git a/core/includes/ajax.inc b/core/includes/ajax.inc
index e11d366b777e..8e1807cf6552 100644
--- a/core/includes/ajax.inc
+++ b/core/includes/ajax.inc
@@ -219,7 +219,8 @@
  * @code
  *   $commands = array();
  *   $commands[] = ajax_command_replace(NULL, $output);
- *   $commands[] = ajax_command_prepend(NULL, theme('status_messages'));
+ *   $status_messages = array('#theme' => 'status_messages');
+ *   $commands[] = ajax_command_prepend(NULL, drupal_render($status_messages));
  *   return array('#type' => 'ajax', '#commands' => $commands);
  * @endcode
  *
diff --git a/core/includes/common.inc b/core/includes/common.inc
index d203772960fb..052242b680bc 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -1376,7 +1376,7 @@ function base_path() {
  * Adds a LINK tag with a distinct 'rel' attribute to the page's HEAD.
  *
  * This function can be called as long the HTML header hasn't been sent, which
- * on normal pages is up through the preprocess step of theme('html'). Adding
+ * on normal pages is up through the preprocess step of _theme('html'). Adding
  * a link will overwrite a prior link with the exact same 'rel' and 'href'
  * attributes.
  *
@@ -2822,8 +2822,15 @@ function drupal_get_library($module, $name = NULL) {
  * into a table. The table must have an ID attribute set. If using
  * theme_table(), the ID may be set as follows:
  * @code
- * $output = theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('id' => 'my-module-table')));
- * return $output;
+ * $table = array(
+ *   '#type' => 'table',
+ *   '#header' => $header,
+ *   '#rows' => $rows,
+ *   '#attributes' => array(
+ *     'id' => 'my-module-table',
+ *   ),
+ * );
+ * return drupal_render($table);
  * @endcode
  *
  * In the theme function for the form, a special class must be added to each
@@ -3654,9 +3661,9 @@ function drupal_render_page($page) {
  *     $elements['#sorted'] = TRUE to avoid sorting them a second time.
  *   - The main render phase to produce #children for this element takes place:
  *     - If this element has #theme defined and #theme is an implemented theme
- *       hook/suggestion then theme() is called and must render both the element
- *       and its children. If #render_children is set, theme() will not be
- *       called. #render_children is usually only set internally by theme() so
+ *       hook/suggestion then _theme() is called and must render both the element
+ *       and its children. If #render_children is set, _theme() will not be
+ *       called. #render_children is usually only set internally by _theme() so
  *       that we can avoid the situation where drupal_render() called from
  *       within a theme preprocess function creates an infinite loop.
  *     - If this element does not have a defined #theme, or the defined #theme
@@ -3674,7 +3681,7 @@ function drupal_render_page($page) {
  *     drupal_process_attached().
  *   - If this element has an array of #theme_wrappers defined and
  *     #render_children is not set, #children is then re-rendered by passing the
- *     element in its current state to theme() successively for each item in
+ *     element in its current state to _theme() successively for each item in
  *     #theme_wrappers. Since #theme and #theme_wrappers hooks often define
  *     variables with the same names it is possible to explicitly override each
  *     attribute passed to each #theme_wrappers hook by setting the hook name as
@@ -3734,7 +3741,7 @@ function drupal_render_page($page) {
  *   The rendered HTML.
  *
  * @see element_info()
- * @see theme()
+ * @see _theme()
  * @see drupal_process_states()
  * @see drupal_process_attached()
  */
@@ -3807,11 +3814,11 @@ function drupal_render(&$elements, $is_recursive_call = FALSE) {
   // property is set, do not call the #theme function to prevent infinite
   // recursion.
   if ($theme_is_implemented && !isset($elements['#render_children'])) {
-    $elements['#children'] = theme($elements['#theme'], $elements);
+    $elements['#children'] = _theme($elements['#theme'], $elements);
 
-    // If theme() returns FALSE this means that the hook in #theme was not found
-    // in the registry and so we need to update our flag accordingly. This is
-    // common for theme suggestions.
+    // If _theme() returns FALSE this means that the hook in #theme was not
+    // found in the registry and so we need to update our flag accordingly. This
+    // is common for theme suggestions.
     $theme_is_implemented = ($elements['#children'] !== FALSE);
   }
 
@@ -3854,7 +3861,7 @@ function drupal_render(&$elements, $is_recursive_call = FALSE) {
       // If the value of a #theme_wrappers item is an array then the theme hook
       // is found in the key of the item and the value contains attribute
       // overrides. Attribute overrides replace key/value pairs in $elements for
-      // only this theme() call. This allows #theme hooks and #theme_wrappers
+      // only this _theme() call. This allows #theme hooks and #theme_wrappers
       // hooks to share variable names without conflict or ambiguity.
       $wrapper_elements = $elements;
       if (is_string($key)) {
@@ -3867,7 +3874,7 @@ function drupal_render(&$elements, $is_recursive_call = FALSE) {
         $wrapper_hook = $value;
       }
 
-      $elements['#children'] = theme($wrapper_hook, $wrapper_elements);
+      $elements['#children'] = _theme($wrapper_hook, $wrapper_elements);
     }
   }
 
diff --git a/core/includes/pager.inc b/core/includes/pager.inc
index 987468973863..f90066c95f1a 100644
--- a/core/includes/pager.inc
+++ b/core/includes/pager.inc
@@ -36,10 +36,10 @@ function pager_find_page($element = 0) {
 }
 
 /**
- * Initializes a pager for theme('pager').
+ * Initializes a pager for _theme('pager').
  *
  * This function sets up the necessary global variables so that future calls
- * to theme('pager') will render a pager that correctly corresponds to the
+ * to _theme('pager') will render a pager that correctly corresponds to the
  * items being displayed.
  *
  * If the items being displayed result from a database query performed using
@@ -67,10 +67,14 @@ function pager_find_page($element = 0) {
  *   // Next, retrieve and display the items for the current page.
  *   $offset = $num_per_page * $page;
  *   $result = mymodule_select("SELECT * FROM data " . $where . " LIMIT %d, %d", $offset, $num_per_page)->fetchAll();
- *   $output = theme('mymodule_results', array('result' => $result));
+ *   $output = drupal_render(
+ *     '#theme' => 'mymodule_results',
+ *     '#result' => $result,
+ *   );
  *
  *   // Finally, display the pager controls, and return.
- *   $output .= theme('pager');
+ *   $pager = array('#theme' => 'pager');
+ *   $output .= drupal_render($pager);
  *   return $output;
  * @endcode
  *
@@ -93,10 +97,16 @@ function pager_find_page($element = 0) {
  *   pager_default_initialize($result->total, $num_per_page);
  *
  *   // Display the search results.
- *   $output = theme('search_results', array('results' => $result->data, 'type' => 'remote'));
+ *   $search_results = array(
+ *     '#theme' => 'search_results',
+ *     '#results' => $result->data,
+ *     '#type' => 'remote',
+ *   );
+ *   $output = drupal_render($search_results);
  *
  *   // Finally, display the pager controls, and return.
- *   $output .= theme('pager');
+ *   $pager = array('#theme' => 'pager');
+ *   $output .= drupal_render($pager);
  *   return $output;
  * @endcode
  *
@@ -148,9 +158,9 @@ function pager_get_query_parameters() {
  *
  * Default template: pager.html.twig.
  *
- * Menu callbacks that display paged query results should call theme('pager') to
- * retrieve a pager control so that users can view other results. Format a list
- * of nearby pages with additional query results.
+ * Menu callbacks that display paged query results should call _theme('pager')
+ * to retrieve a pager control so that users can view other results. Format a
+ * list of nearby pages with additional query results.
  *
  * @param array $variables
  *   An associative array containing:
diff --git a/core/includes/theme.inc b/core/includes/theme.inc
index 4ebe08a20daf..aa558b298332 100644
--- a/core/includes/theme.inc
+++ b/core/includes/theme.inc
@@ -401,15 +401,15 @@ function drupal_find_base_themes($themes, $key) {
  * Generates themed output.
  *
  * All requests for themed output must go through this function (however,
- * calling the theme() function directly is strongly discouraged - see next
- * paragraph). It examines the request and routes it to the appropriate
+ * calling the _theme() function directly is very strongly discouraged - see
+ * next paragraph). It examines the request and routes it to the appropriate
  * @link themeable theme function or template @endlink, by checking the theme
  * registry.
  *
  * Avoid calling this function directly. It is preferable to replace direct
- * calls to the theme() function with calls to drupal_render() by passing a
+ * calls to the _theme() function with calls to drupal_render() by passing a
  * render array with a #theme key to drupal_render(), which in turn calls
- * theme().
+ * _theme().
  *
  * @section sec_theme_hooks Theme Hooks
  * Most commonly, the first argument to this function is the name of the theme
@@ -473,21 +473,21 @@ function drupal_find_base_themes($themes, $key) {
  * hook_theme_suggestions_HOOK_alter() or the generic
  * hook_theme_suggestions_alter(). These alter hooks are used to manipulate an
  * array of suggested alternate theme hooks to use, in reverse order of
- * priority. theme() will use the highest priority implementation that exists.
- * If none exists, theme() will use the implementation for the theme hook it was
- * called with. These suggestions are similar to and are used for similar
- * reasons as calling theme() with an array as the $hook parameter (see below).
+ * priority. _theme() will use the highest priority implementation that exists.
+ * If none exists, _theme() will use the implementation for the theme hook it
+ * was called with. These suggestions are similar to and are used for similar
+ * reasons as calling _theme() with an array as the $hook parameter (see below).
  * The difference is whether the suggestions are determined by the code that
- * calls theme() or by altering the suggestions via the suggestion alter hooks.
+ * calls _theme() or by altering the suggestions via the suggestion alter hooks.
  *
  * @param $hook
  *   The name of the theme hook to call. If the name contains a
  *   double-underscore ('__') and there isn't an implementation for the full
  *   name, the part before the '__' is checked. This allows a fallback to a
- *   more generic implementation. For example, if theme('links__node', ...) is
+ *   more generic implementation. For example, if _theme('links__node', ...) is
  *   called, but there is no implementation of that theme hook, then the
  *   'links' implementation is used. This process is iterative, so if
- *   theme('links__contextual__node', ...) is called, theme() checks for the
+ *   _theme('links__contextual__node', ...) is called, _theme() checks for the
  *   following implementations, and uses the first one that exists:
  *   - links__contextual__node
  *   - links__contextual
@@ -495,7 +495,7 @@ function drupal_find_base_themes($themes, $key) {
  *   This allows themes to create specific theme implementations for named
  *   objects and contexts of otherwise generic theme hooks. The $hook parameter
  *   may also be an array, in which case the first theme hook that has an
- *   implementation is used. This allows for the code that calls theme() to
+ *   implementation is used. This allows for the code that calls _theme() to
  *   explicitly specify the fallback order in a situation where using the '__'
  *   convention is not desired or is insufficient.
  * @param $variables
@@ -514,13 +514,13 @@ function drupal_find_base_themes($themes, $key) {
  * @see hook_theme()
  * @see template_preprocess()
  */
-function theme($hook, $variables = array()) {
+function _theme($hook, $variables = array()) {
   static $default_attributes;
   // If called before all modules are loaded, we do not necessarily have a full
   // theme registry to work with, and therefore cannot process the theme
   // request properly. See also \Drupal\Core\Theme\Registry::get().
   if (!\Drupal::moduleHandler()->isLoaded() && !defined('MAINTENANCE_MODE')) {
-    throw new Exception(t('theme() may not be called until all modules are loaded.'));
+    throw new Exception(t('_theme() may not be called until all modules are loaded.'));
   }
 
   /** @var \Drupal\Core\Utility\ThemeRegistry $theme_registry */
@@ -558,7 +558,7 @@ function theme($hook, $variables = array()) {
         watchdog('theme', 'Theme hook %hook not found.', array('%hook' => $hook), WATCHDOG_WARNING);
       }
       // There is no theme implementation for the hook passed. Return FALSE so
-      // the function calling theme() can differentiate between a hook that
+      // the function calling _theme() can differentiate between a hook that
       // exists and renders an empty string and a hook that is not implemented.
       return FALSE;
     }
@@ -622,7 +622,7 @@ function theme($hook, $variables = array()) {
 
   // Invoke hook_theme_suggestions_HOOK().
   $suggestions = Drupal::moduleHandler()->invokeAll('theme_suggestions_' . $base_theme_hook, array($variables));
-  // If theme() was invoked with a direct theme suggestion like
+  // If _theme() was invoked with a direct theme suggestion like
   // '#theme' => 'node__article', add it to the suggestions array before
   // invoking suggestion alter hooks.
   if (isset($info['base hook'])) {
@@ -638,8 +638,8 @@ function theme($hook, $variables = array()) {
   \Drupal::moduleHandler()->alter($hooks, $suggestions, $variables, $base_theme_hook);
 
   // Check if each suggestion exists in the theme registry, and if so,
-  // use it instead of the hook that theme() was called with. For example, a
-  // function may call theme('node', ...), but a module can add
+  // use it instead of the hook that _theme() was called with. For example, a
+  // function may call _theme('node', ...), but a module can add
   // 'node__article' as a suggestion via hook_theme_suggestions_HOOK_alter(),
   // enabling a theme to have an alternate template file for article nodes.
   foreach (array_reverse($suggestions) as $suggestion) {
@@ -1352,7 +1352,7 @@ function template_preprocess_links(&$variables) {
  *     to an empty string, but can be set to NULL for the attribute to be
  *     omitted. Usually, neither omission nor an empty string satisfies
  *     accessibility requirements, so it is strongly encouraged for code
- *     calling theme('image') to pass a meaningful value for this variable.
+ *     calling _theme('image') to pass a meaningful value for this variable.
  *     - http://www.w3.org/TR/REC-html40/struct/objects.html#h-13.8
  *     - http://www.w3.org/TR/xhtml1/dtds.html
  *     - http://dev.w3.org/html5/spec/Overview.html#alt
@@ -1956,10 +1956,10 @@ function _theme_table_cell($cell, $header = FALSE) {
  * This function is called for theme hooks implemented as templates only, not
  * for theme hooks implemented as functions. This preprocess function is the
  * first in the sequence of preprocessing functions that are called when
- * preparing variables for a template. See theme() for more details about the
+ * preparing variables for a template. See _theme() for more details about the
  * full sequence.
  *
- * @see theme()
+ * @see _theme()
  */
 function template_preprocess(&$variables, $hook, $info) {
   // Tell all templates where they are located.
@@ -2580,10 +2580,10 @@ function drupal_common_theme() {
       // HTML 4 and XHTML 1.0 always require an alt attribute. The HTML 5 draft
       // allows the alt attribute to be omitted in some cases. Therefore,
       // default the alt attribute to an empty string, but allow code calling
-      // theme('image') to pass explicit NULL for it to be omitted. Usually,
+      // _theme('image') to pass explicit NULL for it to be omitted. Usually,
       // neither omission nor an empty string satisfies accessibility
       // requirements, so it is strongly encouraged for code calling
-      // theme('image') to pass a meaningful value for the alt variable.
+      // _theme('image') to pass a meaningful value for the alt variable.
       // - http://www.w3.org/TR/REC-html40/struct/objects.html#h-13.8
       // - http://www.w3.org/TR/xhtml1/dtds.html
       // - http://dev.w3.org/html5/spec/Overview.html#alt
diff --git a/core/lib/Drupal/Core/Extension/UpdateModuleHandler.php b/core/lib/Drupal/Core/Extension/UpdateModuleHandler.php
index a8334b84c9f2..828508bc0ad6 100644
--- a/core/lib/Drupal/Core/Extension/UpdateModuleHandler.php
+++ b/core/lib/Drupal/Core/Extension/UpdateModuleHandler.php
@@ -24,7 +24,7 @@ public function getImplementations($hook) {
     if (substr($hook, -6) === '_alter') {
       return array();
     }
-    // theme() is called during updates and fires hooks, so whitelist the
+    // _theme() is called during updates and fires hooks, so whitelist the
     // system module.
     if (substr($hook, 0, 6) == 'theme_') {
       return array('system');
diff --git a/core/lib/Drupal/Core/Theme/Registry.php b/core/lib/Drupal/Core/Theme/Registry.php
index b0fed73c205d..1d3cb22960a2 100644
--- a/core/lib/Drupal/Core/Theme/Registry.php
+++ b/core/lib/Drupal/Core/Theme/Registry.php
@@ -59,12 +59,12 @@ class Registry implements DestructableInterface {
    *     from; e.g., 'module' for theme hook 'node' of Node module.
    *   - name: The name of the extension the original theme hook originates
    *     from; e.g., 'node' for theme hook 'node' of Node module.
-   *   - theme path: The effective path_to_theme() during theme(), available as
+   *   - theme path: The effective path_to_theme() during _theme(), available as
    *     'directory' variable in templates.
    *       functions, it should point to the respective theme. For templates,
    *       it should point to the directory that contains the template.
    *   - includes: (optional) An array of include files to load when the theme
-   *     hook is executed by theme().
+   *     hook is executed by _theme().
    *   - file: (optional) A filename to add to 'includes', either prefixed with
    *     the value of 'path', or the path of the extension implementing
    *     hook_theme().
@@ -291,7 +291,7 @@ public function getBaseHook($hook) {
    * for base hooks (e.g., 'block__node' for the base hook 'block') need to be
    * determined based on the full registry and classified as 'base hook'.
    *
-   * @see theme()
+   * @see _theme()
    * @see hook_theme_registry_alter()
    *
    * @return \Drupal\Core\Utility\ThemeRegistry
@@ -374,7 +374,7 @@ protected function build() {
    *     in hook_theme(). If there is more than one implementation and
    *     'render element' is not specified in a later one, then the previous
    *     definition is kept.
-   *   - 'preprocess functions': See theme() for detailed documentation.
+   *   - 'preprocess functions': See _theme() for detailed documentation.
    * @param string $name
    *   The name of the module, theme engine, base theme engine, theme or base
    *   theme implementing hook_theme().
@@ -391,7 +391,7 @@ protected function build() {
    *   The directory where $name is. For example, modules/system or
    *   themes/bartik.
    *
-   * @see theme()
+   * @see _theme()
    * @see hook_theme()
    * @see list_themes()
    */
@@ -486,7 +486,7 @@ protected function processExtension(&$cache, $name, $type, $theme, $path) {
           }
           foreach ($prefixes as $prefix) {
             // Only use non-hook-specific variable preprocessors for theming
-            // hooks implemented as templates. See theme().
+            // hooks implemented as templates. See _theme().
             if (isset($info['template']) && function_exists($prefix . '_preprocess')) {
               $info['preprocess functions'][] = $prefix . '_preprocess';
             }
@@ -522,7 +522,7 @@ protected function processExtension(&$cache, $name, $type, $theme, $path) {
             $cache[$hook]['preprocess functions'] = array();
           }
           // Only use non-hook-specific variable preprocessors for theme hooks
-          // implemented as templates. See theme().
+          // implemented as templates. See _theme().
           if (isset($info['template']) && function_exists($name . '_preprocess')) {
             $cache[$hook]['preprocess functions'][] = $name . '_preprocess';
           }
diff --git a/core/lib/Drupal/Core/Utility/ThemeRegistry.php b/core/lib/Drupal/Core/Utility/ThemeRegistry.php
index 8d3e1f88d6c8..dbd44e910b51 100644
--- a/core/lib/Drupal/Core/Utility/ThemeRegistry.php
+++ b/core/lib/Drupal/Core/Utility/ThemeRegistry.php
@@ -26,7 +26,7 @@ class ThemeRegistry extends CacheCollector implements DestructableInterface {
   /**
    * Whether the partial registry can be persisted to the cache.
    *
-   * This is only allowed if all modules and the request method is GET. theme()
+   * This is only allowed if all modules and the request method is GET. _theme()
    * should be very rarely called on POST requests and this avoids polluting
    * the runtime cache.
    */
diff --git a/core/modules/block/block.api.php b/core/modules/block/block.api.php
index c6add64a93eb..065a1cd7d75c 100644
--- a/core/modules/block/block.api.php
+++ b/core/modules/block/block.api.php
@@ -20,7 +20,7 @@
  * If the module wishes to act on the rendered HTML of the block rather than
  * the structured content array, it may use this hook to add a #post_render
  * callback. Alternatively, it could also implement hook_preprocess_HOOK() for
- * block.html.twig. See drupal_render() and theme() documentation respectively
+ * block.html.twig. See drupal_render() and _theme() documentation respectively
  * for details.
  *
  * In addition to hook_block_view_alter(), which is called for all blocks, there
diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module
index cb98f722b15e..541ae92760b0 100644
--- a/core/modules/comment/comment.module
+++ b/core/modules/comment/comment.module
@@ -1395,7 +1395,6 @@ function template_preprocess_comment(&$variables) {
   $variables['commented_entity'] = $commented_entity;
 
   $account = comment_prepare_author($comment);
-  // @todo Do not call theme() here. We do this for purposes of t().
   $username = array(
     '#theme' => 'username',
     '#account' => $account,
@@ -1446,7 +1445,6 @@ function template_preprocess_comment(&$variables) {
     $comment_parent = $comment->getParentComment();
     $account_parent = comment_prepare_author($comment_parent);
     $variables['parent_comment'] = $comment_parent;
-    // @todo Do not call theme() here. We do this for purposes of t().
     $username = array(
       '#theme' => 'username',
       '#account' => $account_parent,
diff --git a/core/modules/image/image.module b/core/modules/image/image.module
index e5f52ed15817..00c2690fb14b 100644
--- a/core/modules/image/image.module
+++ b/core/modules/image/image.module
@@ -143,10 +143,10 @@ function image_theme() {
       // HTML 4 and XHTML 1.0 always require an alt attribute. The HTML 5 draft
       // allows the alt attribute to be omitted in some cases. Therefore,
       // default the alt attribute to an empty string, but allow code calling
-      // theme('image_style') to pass explicit NULL for it to be omitted.
+      // _theme('image_style') to pass explicit NULL for it to be omitted.
       // Usually, neither omission nor an empty string satisfies accessibility
       // requirements, so it is strongly encouraged for code calling
-      // theme('image_style') to pass a meaningful value for the alt variable.
+      // _theme('image_style') to pass a meaningful value for the alt variable.
       // - http://www.w3.org/TR/REC-html40/struct/objects.html#h-13.8
       // - http://www.w3.org/TR/xhtml1/dtds.html
       // - http://dev.w3.org/html5/spec/Overview.html#alt
@@ -349,7 +349,7 @@ function image_style_options($include_empty = TRUE) {
  *     to an empty string, but can be set to NULL for the attribute to be
  *     omitted. Usually, neither omission nor an empty string satisfies
  *     accessibility requirements, so it is strongly encouraged for code calling
- *     theme('image_style') to pass a meaningful value for this variable.
+ *     _theme('image_style') to pass a meaningful value for this variable.
  *     - http://www.w3.org/TR/REC-html40/struct/objects.html#h-13.8
  *     - http://www.w3.org/TR/xhtml1/dtds.html
  *     - http://dev.w3.org/html5/spec/Overview.html#alt
diff --git a/core/modules/node/node.api.php b/core/modules/node/node.api.php
index ab7811e64940..20f62576b158 100644
--- a/core/modules/node/node.api.php
+++ b/core/modules/node/node.api.php
@@ -820,7 +820,7 @@ function hook_node_view(\Drupal\node\NodeInterface $node, \Drupal\Core\Entity\Di
  * If the module wishes to act on the rendered HTML of the node rather than the
  * structured content array, it may use this hook to add a #post_render
  * callback.  Alternatively, it could also implement hook_preprocess_HOOK() for
- * node.html.twig. See drupal_render() and theme() documentation respectively
+ * node.html.twig. See drupal_render() and _theme() documentation respectively
  * for details.
  *
  * @param $build
diff --git a/core/modules/simpletest/lib/Drupal/simpletest/DrupalUnitTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/DrupalUnitTestBase.php
index d44064e30cc6..cab4b9ea1201 100644
--- a/core/modules/simpletest/lib/Drupal/simpletest/DrupalUnitTestBase.php
+++ b/core/modules/simpletest/lib/Drupal/simpletest/DrupalUnitTestBase.php
@@ -358,7 +358,7 @@ protected function enableModules(array $modules) {
     // Update the kernel to make their services available.
     $this->kernel->updateModules($module_filenames, $module_filenames);
 
-    // Ensure isLoaded() is TRUE in order to make theme() work.
+    // Ensure isLoaded() is TRUE in order to make _theme() work.
     // Note that the kernel has rebuilt the container; this $module_handler is
     // no longer the $module_handler instance from above.
     $module_handler = $this->container->get('module_handler');
@@ -392,7 +392,7 @@ protected function disableModules(array $modules) {
     // Update the kernel to remove their services.
     $this->kernel->updateModules($module_filenames, $module_filenames);
 
-    // Ensure isLoaded() is TRUE in order to make theme() work.
+    // Ensure isLoaded() is TRUE in order to make _theme() work.
     // Note that the kernel has rebuilt the container; this $module_handler is
     // no longer the $module_handler instance from above.
     $module_handler = $this->container->get('module_handler');
diff --git a/core/modules/simpletest/lib/Drupal/simpletest/Tests/DrupalUnitTestBaseTest.php b/core/modules/simpletest/lib/Drupal/simpletest/Tests/DrupalUnitTestBaseTest.php
index d8275d3a104b..f2beeaaad98e 100644
--- a/core/modules/simpletest/lib/Drupal/simpletest/Tests/DrupalUnitTestBaseTest.php
+++ b/core/modules/simpletest/lib/Drupal/simpletest/Tests/DrupalUnitTestBaseTest.php
@@ -249,7 +249,7 @@ function testEnableModulesFixedList() {
   }
 
   /**
-   * Tests that theme() works right after loading a module.
+   * Tests that _theme() works right after loading a module.
    */
   function testEnableModulesTheme() {
     $original_element = $element = array(
@@ -258,7 +258,7 @@ function testEnableModulesTheme() {
       '#attributes' => array(),
     );
     $this->enableModules(array('system'));
-    // theme() throws an exception if modules are not loaded yet.
+    // _theme() throws an exception if modules are not loaded yet.
     $this->assertTrue(drupal_render($element));
 
     $element = $original_element;
diff --git a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php
index af667919adff..702ef69a886b 100644
--- a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php
+++ b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php
@@ -2994,7 +2994,7 @@ protected function assertNoTitle($title, $message = '', $group = 'Other') {
    *   TRUE on pass, FALSE on fail.
    */
   protected function assertThemeOutput($callback, array $variables = array(), $expected, $message = '', $group = 'Other') {
-    $output = theme($callback, $variables);
+    $output = _theme($callback, $variables);
     $this->verbose('Variables:' . '<pre>' .  check_plain(var_export($variables, TRUE)) . '</pre>'
       . '<hr />' . 'Result:' . '<pre>' .  check_plain(var_export($output, TRUE)) . '</pre>'
       . '<hr />' . 'Expected:' . '<pre>' .  check_plain(var_export($expected, TRUE)) . '</pre>'
diff --git a/core/modules/system/entity.api.php b/core/modules/system/entity.api.php
index 1a3973dd329c..84bfad874719 100644
--- a/core/modules/system/entity.api.php
+++ b/core/modules/system/entity.api.php
@@ -477,7 +477,7 @@ function hook_entity_view(\Drupal\Core\Entity\EntityInterface $entity, \Drupal\C
  * structured content array, it may use this hook to add a #post_render
  * callback. Alternatively, it could also implement hook_preprocess_HOOK() for
  * the particular entity type template, if there is one (e.g., node.html.twig).
- * See drupal_render() and theme() for details.
+ * See drupal_render() and _theme() for details.
  *
  * @param $build
  *   A renderable array representing the entity content.
diff --git a/core/modules/system/form.api.php b/core/modules/system/form.api.php
index 36c4c85300e0..5ae2d69c310e 100644
--- a/core/modules/system/form.api.php
+++ b/core/modules/system/form.api.php
@@ -110,7 +110,11 @@ function callback_batch_finished($success, $results, $operations) {
     $message = t("!count items were processed.", array(
       '!count' => count($results),
       ));
-    $message .= theme('item_list', array('items' => $results));
+    $list = array(
+      '#theme' => 'item_list',
+      '#items' => $results,
+    );
+    $message .= drupal_render($list);
     drupal_set_message($message);
   }
   else {
diff --git a/core/modules/system/lib/Drupal/system/Tests/Theme/TableTest.php b/core/modules/system/lib/Drupal/system/Tests/Theme/TableTest.php
index 1a9d3d172540..a5502e7b8a73 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Theme/TableTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Theme/TableTest.php
@@ -27,7 +27,13 @@ public static function getInfo() {
   function testThemeTableStickyHeaders() {
     $header = array('one', 'two', 'three');
     $rows = array(array(1,2,3), array(4,5,6), array(7,8,9));
-    $this->content = theme('table', array('header' => $header, 'rows' => $rows, 'sticky' => TRUE));
+    $table = array(
+      '#type' => 'table',
+      '#header' => $header,
+      '#rows' => $rows,
+      '#sticky' => TRUE,
+    );
+    $this->content = drupal_render($table);
     $js = _drupal_add_js();
     $this->assertTrue(isset($js['core/misc/tableheader.js']), 'tableheader.js was included when $sticky = TRUE.');
     $this->assertRaw('sticky-enabled',  'Table has a class of sticky-enabled when $sticky = TRUE.');
@@ -43,7 +49,16 @@ function testThemeTableNoStickyHeaders() {
     $attributes = array();
     $caption = NULL;
     $colgroups = array();
-    $this->content = theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => $attributes, 'caption' => $caption, 'colgroups' => $colgroups, 'sticky' => FALSE));
+    $table = array(
+      '#type' => 'table',
+      '#header' => $header,
+      '#rows' => $rows,
+      '#attributes' => $attributes,
+      '#caption' => $caption,
+      '#colgroups' => $colgroups,
+      '#sticky' => FALSE,
+    );
+    $this->content = drupal_render($table);
     $js = _drupal_add_js();
     $this->assertFalse(isset($js['core/misc/tableheader.js']), 'tableheader.js was not included because $sticky = FALSE.');
     $this->assertNoRaw('sticky-enabled',  'Table does not have a class of sticky-enabled because $sticky = FALSE.');
@@ -62,7 +77,13 @@ function testThemeTableWithEmptyMessage() {
         'colspan' => 2,
       ),
     );
-    $this->content = theme('table', array('header' => $header, 'rows' => array(), 'empty' => t('No strings available.')));
+    $table = array(
+      '#type' => 'table',
+      '#header' => $header,
+      '#rows' => array(),
+      '#empty' => t('No strings available.'),
+    );
+    $this->content = drupal_render($table);
     $this->assertRaw('<tr class="odd"><td colspan="3" class="empty message">No strings available.</td>', 'Correct colspan was set on empty message.');
     $this->assertRaw('<thead><tr><th>Header 1</th>', 'Table header was printed.');
   }
@@ -77,7 +98,11 @@ function testThemeTableWithNoStriping() {
         'no_striping' => TRUE,
       ),
     );
-    $this->content = theme('table', array('rows' => $rows));
+    $table = array(
+      '#type' => 'table',
+      '#rows' => $rows,
+    );
+    $this->content = drupal_render($table);
     $this->assertNoRaw('class="odd"', 'Odd/even classes were not added because $no_striping = TRUE.');
     $this->assertNoRaw('no_striping', 'No invalid no_striping HTML attribute was printed.');
   }
diff --git a/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeTest.php b/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeTest.php
index a07533db84b9..638d8e568d23 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeTest.php
@@ -41,7 +41,7 @@ function setUp() {
    * Render arrays that use a render element and templates (and hence call
    * template_preprocess()) must ensure the attributes at different occasions
    * are all merged correctly:
-   *   - $variables['attributes'] as passed in to theme()
+   *   - $variables['attributes'] as passed in to _theme()
    *   - the render element's #attributes
    *   - any attributes set in the template's preprocessing function
    */
@@ -58,21 +58,21 @@ function testAttributeMerging() {
   }
 
   /**
-   * Test that theme() returns expected data types.
+   * Test that _theme() returns expected data types.
    */
   function testThemeDataTypes() {
-    // theme_test_false is an implemented theme hook so theme() should return a
+    // theme_test_false is an implemented theme hook so _theme() should return a
     // string, even though the theme function itself can return anything.
     $foos = array('null' => NULL, 'false' => FALSE, 'integer' => 1, 'string' => 'foo');
     foreach ($foos as $type => $example) {
-      $output = theme('theme_test_foo', array('foo' => $example));
-      $this->assertTrue(is_string($output), format_string('theme() returns a string for data type !type.', array('!type' => $type)));
+      $output = _theme('theme_test_foo', array('foo' => $example));
+      $this->assertTrue(is_string($output), format_string('_theme() returns a string for data type !type.', array('!type' => $type)));
     }
 
-    // suggestionnotimplemented is not an implemented theme hook so theme()
+    // suggestionnotimplemented is not an implemented theme hook so _theme()
     // should return FALSE instead of a string.
-    $output = theme(array('suggestionnotimplemented'));
-    $this->assertIdentical($output, FALSE, 'theme() returns FALSE when a hook suggestion is not implemented.');
+    $output = _theme(array('suggestionnotimplemented'));
+    $this->assertIdentical($output, FALSE, '_theme() returns FALSE when a hook suggestion is not implemented.');
   }
 
   /**
diff --git a/core/modules/system/lib/Drupal/system/Tests/Theme/TwigDebugMarkupTest.php b/core/modules/system/lib/Drupal/system/Tests/Theme/TwigDebugMarkupTest.php
index ccfd353c02bd..32a1362f84a7 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Theme/TwigDebugMarkupTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Theme/TwigDebugMarkupTest.php
@@ -48,9 +48,9 @@ function testTwigDebugMarkup() {
 
     // Create a node and test different features of the debug markup.
     $node = $this->drupalCreateNode();
-    $output = theme('node', node_view($node));
+    $output = _theme('node', node_view($node));
     $this->assertTrue(strpos($output, '<!-- THEME DEBUG -->') !== FALSE, 'Twig debug markup found in theme output when debug is enabled.');
-    $this->assertTrue(strpos($output, "CALL: theme('node')") !== FALSE, 'Theme call information found.');
+    $this->assertTrue(strpos($output, "CALL: _theme('node')") !== FALSE, 'Theme call information found.');
     $this->assertTrue(strpos($output, 'x node--1' . $extension) !== FALSE, 'Node ID specific template shown as current template.');
     $this->assertTrue(strpos($output, '* node' . $extension) !== FALSE, 'Base template file found.');
     $template_filename = $templates['node__1']['path'] . '/' . $templates['node__1']['template'] . $extension;
@@ -59,7 +59,7 @@ function testTwigDebugMarkup() {
     // Create another node and make sure the template suggestions shown in the
     // debug markup are correct.
     $node2 = $this->drupalCreateNode();
-    $output = theme('node', node_view($node2));
+    $output = _theme('node', node_view($node2));
     $this->assertTrue(strpos($output, '* node--2' . $extension) !== FALSE, 'Node ID specific template suggestion found.');
     $this->assertTrue(strpos($output, 'x node' . $extension) !== FALSE, 'Base template file shown as current template.');
 
@@ -68,7 +68,7 @@ function testTwigDebugMarkup() {
     $this->rebuildContainer();
     $this->resetAll();
 
-    $output = theme('node', node_view($node));
+    $output = _theme('node', node_view($node));
     $this->assertFalse(strpos($output, '<!-- THEME DEBUG -->') !== FALSE, 'Twig debug markup not found in theme output when debug is disabled.');
   }
 
diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php
index 59dfce0fb78c..2530db5c4cb2 100644
--- a/core/modules/system/system.api.php
+++ b/core/modules/system/system.api.php
@@ -1206,8 +1206,8 @@ function hook_permission() {
  * - They can specify how a particular render array is to be rendered as HTML.
  *   This is usually the case if the theme function is assigned to the render
  *   array's #theme property.
- * - They can return HTML for default calls to theme().
- * - They can return HTML for calls to theme() for a theme suggestion.
+ * - They can return HTML for default calls to _theme().
+ * - They can return HTML for calls to _theme() for a theme suggestion.
  *
  * @param array $existing
  *   An array of existing implementations that may be used for override
@@ -1234,18 +1234,18 @@ function hook_permission() {
  * @return array
  *   An associative array of information about theme implementations. The keys
  *   on the outer array are known as "theme hooks". For simple theme
- *   implementations for regular calls to theme(), the theme hook is the first
+ *   implementations for regular calls to _theme(), the theme hook is the first
  *   argument. For theme suggestions, instead of the array key being the base
  *   theme hook, the key is a theme suggestion name with the format
  *   'base_hook_name__sub_hook_name'. For render elements, the key is the
  *   machine name of the render element. The array values are themselves arrays
  *   containing information about the theme hook and its implementation. Each
- *   information array must contain either a 'variables' element (for theme()
+ *   information array must contain either a 'variables' element (for _theme()
  *   calls) or a 'render element' element (for render elements), but not both.
  *   The following elements may be part of each information array:
- *   - variables: Used for theme() call items only: an array of variables,
+ *   - variables: Used for _theme() call items only: an array of variables,
  *     where the array keys are the names of the variables, and the array
- *     values are the default values if they are not passed into theme().
+ *     values are the default values if they are not passed into _theme().
  *     Template implementations receive each array key as a variable in the
  *     template file (so they must be legal PHP/Twig variable names). Function
  *     implementations are passed the variables in a single $variables function
@@ -1273,7 +1273,7 @@ function hook_permission() {
  *     registers the 'node' theme hook, 'theme_node' will be assigned to its
  *     function. If the chameleon theme registers the node hook, it will be
  *     assigned 'chameleon_node' as its function.
- *   - base hook: Used for theme() suggestions only: the base theme hook name.
+ *   - base hook: Used for _theme() suggestions only: the base theme hook name.
  *     Instead of this suggestion's implementation being used directly, the base
  *     hook will be invoked with this implementation as its first suggestion.
  *     The base hook's files will be included and the base hook's preprocess
@@ -1283,14 +1283,14 @@ function hook_permission() {
  *     suggestion may be used in place of this suggestion. If after
  *     hook_theme_suggestions_HOOK() this suggestion remains the first
  *     suggestion, then this suggestion's function or template will be used to
- *     generate the output for theme().
+ *     generate the output for _theme().
  *   - pattern: A regular expression pattern to be used to allow this theme
  *     implementation to have a dynamic name. The convention is to use __ to
  *     differentiate the dynamic portion of the theme. For example, to allow
  *     forums to be themed individually, the pattern might be: 'forum__'. Then,
  *     when the forum is themed, call:
  *     @code
- *     theme(array('forum__' . $tid, 'forum'), $forum)
+ *     _theme(array('forum__' . $tid, 'forum'), $forum)
  *     @endcode
  *   - preprocess functions: A list of functions used to preprocess this data.
  *     Ordinarily this won't be used; it's automatically filled in. By default,
diff --git a/core/modules/system/tests/modules/theme_test/lib/Drupal/theme_test/EventSubscriber/ThemeTestSubscriber.php b/core/modules/system/tests/modules/theme_test/lib/Drupal/theme_test/EventSubscriber/ThemeTestSubscriber.php
index 2e5417d4804e..d3676e0b0933 100644
--- a/core/modules/system/tests/modules/theme_test/lib/Drupal/theme_test/EventSubscriber/ThemeTestSubscriber.php
+++ b/core/modules/system/tests/modules/theme_test/lib/Drupal/theme_test/EventSubscriber/ThemeTestSubscriber.php
@@ -42,7 +42,12 @@ public function onRequest(GetResponseEvent $event) {
       // theme_test_request_listener_page_callback() to test that even when the
       // theme system is initialized this early, it is still capable of
       // returning output and theming the page as a whole.
-      $GLOBALS['theme_test_output'] = theme('more_link', array('url' => 'user', 'title' => 'Themed output generated in a KernelEvents::REQUEST listener'));
+      $more_link = array(
+        '#theme' => 'more_link',
+        '#url' => 'user',
+        '#title' => 'Themed output generated in a KernelEvents::REQUEST listener',
+      );
+      $GLOBALS['theme_test_output'] = drupal_render($more_link);
     }
   }
 
diff --git a/core/modules/system/tests/modules/theme_test/lib/Drupal/theme_test/ThemeTestController.php b/core/modules/system/tests/modules/theme_test/lib/Drupal/theme_test/ThemeTestController.php
index 29bfa4eff5bd..3684793637ab 100644
--- a/core/modules/system/tests/modules/theme_test/lib/Drupal/theme_test/ThemeTestController.php
+++ b/core/modules/system/tests/modules/theme_test/lib/Drupal/theme_test/ThemeTestController.php
@@ -55,7 +55,7 @@ public function testInfoStylesheets() {
    *   A render array containing a theme override.
    */
   public function testTemplate() {
-    return theme('theme_test_template_test');
+    return _theme('theme_test_template_test');
   }
 
   /**
@@ -65,12 +65,12 @@ public function testTemplate() {
    *   An HTML string containing the themed output.
    */
   public function testSuggestion() {
-    return theme(array('theme_test__suggestion', 'theme_test'), array());
+    return _theme(array('theme_test__suggestion', 'theme_test'), array());
   }
 
 /**
  * This is for testing that the theme can have hook_*_alter() implementations
- * that run during page callback execution, even before theme() is called for
+ * that run during page callback execution, even before _theme() is called for
  * the first time.
  *
  * @return string
diff --git a/core/modules/system/tests/modules/theme_test/theme_test.module b/core/modules/system/tests/modules/theme_test/theme_test.module
index bdb1404695a6..4e1fc13ad92a 100644
--- a/core/modules/system/tests/modules/theme_test/theme_test.module
+++ b/core/modules/system/tests/modules/theme_test/theme_test.module
@@ -102,14 +102,14 @@ function theme_test_page_alter(&$page) {
 }
 
 /**
- * Theme function for testing theme('theme_test_foo').
+ * Theme function for testing _theme('theme_test_foo').
  */
 function theme_theme_test_foo($variables) {
   return $variables['foo'];
 }
 
 /**
- * Theme function for testing theme('theme_test_function_template_override').
+ * Theme function for testing _theme('theme_test_function_template_override').
  */
 function theme_theme_test_function_template_override($variables) {
   return 'theme_test_function_template_override test failed.';
@@ -132,8 +132,8 @@ function template_preprocess_theme_test_render_element(&$variables) {
  * Theme function for testing rendering of child elements via drupal_render().
  *
  * Theme hooks defining a 'render element' add an internal '#render_children'
- * property. When this property is found, drupal_render() avoids calling theme()
- * on the top-level element to prevent infinite recursion.
+ * property. When this property is found, drupal_render() avoids calling
+ * _theme() on the top-level element to prevent infinite recursion.
  *
  * @param array $variables
  *   An associative array containing:
diff --git a/core/modules/system/tests/modules/twig_theme_test/lib/Drupal/twig_theme_test/TwigThemeTestController.php b/core/modules/system/tests/modules/twig_theme_test/lib/Drupal/twig_theme_test/TwigThemeTestController.php
index b46f94876422..45654ea206dc 100644
--- a/core/modules/system/tests/modules/twig_theme_test/lib/Drupal/twig_theme_test/TwigThemeTestController.php
+++ b/core/modules/system/tests/modules/twig_theme_test/lib/Drupal/twig_theme_test/TwigThemeTestController.php
@@ -16,7 +16,7 @@ class TwigThemeTestController {
    * Menu callback for testing PHP variables in a Twig template.
    */
   public function phpVariablesRender() {
-    return theme('twig_theme_test_php_variables');
+    return _theme('twig_theme_test_php_variables');
   }
 
   /**
diff --git a/core/modules/system/theme.api.php b/core/modules/system/theme.api.php
index db11a21c4048..a5d617de822a 100644
--- a/core/modules/system/theme.api.php
+++ b/core/modules/system/theme.api.php
@@ -9,19 +9,41 @@
  * layer. Each theme can take control over most of Drupal's output, and
  * has complete control over the CSS.
  *
- * Inside Drupal, the theme layer is utilized by the use of the theme()
+ * Inside Drupal, the theme layer is utilized by the use of the _theme()
  * function, which is passed the name of a component (the theme hook)
  * and an array of variables. For example,
- * theme('table', array('header' => $header, 'rows' => $rows));
- * Additionally, the theme() function can take an array of theme
+ * _theme('table', array('header' => $header, 'rows' => $rows));
+ * Additionally, the _theme() function can take an array of theme
  * hooks, which can be used to provide 'fallback' implementations to
  * allow for more specific control of output. For example, the function:
- * theme(array('table__foo', 'table'), $variables) would look to see if
+ * _theme(array('table__foo', 'table'), $variables) would look to see if
  * 'table__foo' is registered anywhere; if it is not, it would 'fall back'
  * to the generic 'table' implementation. This can be used to attach specific
  * theme functions to named objects, allowing the themer more control over
  * specific types of output.
  *
+ * Calling the _theme() function directly is highly discouraged. Building a
+ * renderable array is preferred. For example, rather than calling
+ * _theme('table', array()) in-place, one can assemble a renderable array as
+ * follows:
+ *
+ * @code
+ * $table = array(
+ *   '#type' => 'table',
+ *   '#header' => '',
+ *   '#rows' => array(),
+ * );
+ * @endcode
+ *
+ * Note that a table is defined as a type as well as a theme function. Building
+ * it as a type is preferred. The $table array can simply be passed along as
+ * a renderable array in a page build process. If necessary, the array may be
+ * rendered to a string by calling drupal_render().
+ *
+ * @code
+ * $output = drupal_render($table);
+ * @endcode
+ *
  * As of Drupal 6, every theme hook is required to be registered by the
  * module that owns it, so that Drupal can tell what to do with it and
  * to make it simple for themes to identify and override the behavior
@@ -55,10 +77,11 @@
  *
  * The theme system is described and defined in theme.inc.
  *
- * @see theme()
+ * @see _theme()
  * @see hook_theme()
  * @see hooks
  * @see callbacks
+ * @see system_element_info()
  *
  * @} End of "defgroup themeable".
  */
@@ -98,7 +121,7 @@ function hook_form_system_theme_settings_alter(&$form, &$form_state) {
  * preprocess variables for a specific theme hook, whether implemented as a
  * template or function.
  *
- * For more detailed information, see theme().
+ * For more detailed information, see _theme().
  *
  * @param $variables
  *   The variables array (modify in place).
@@ -146,7 +169,7 @@ function hook_preprocess(&$variables, $hook) {
  * hook. It should only be used if a module needs to override or add to the
  * theme preprocessing for a theme hook it didn't define.
  *
- * For more detailed information, see theme().
+ * For more detailed information, see _theme().
  *
  * @param $variables
  *   The variables array (modify in place).
diff --git a/core/modules/taxonomy/taxonomy.api.php b/core/modules/taxonomy/taxonomy.api.php
index 5894b3a9c6bd..1c32203e99f2 100644
--- a/core/modules/taxonomy/taxonomy.api.php
+++ b/core/modules/taxonomy/taxonomy.api.php
@@ -282,7 +282,7 @@ function hook_taxonomy_term_view(\Drupal\taxonomy\Entity\Term $term, \Drupal\Cor
  * structured content array, it may use this hook to add a #post_render
  * callback. Alternatively, it could also implement
  * hook_preprocess_HOOK() for taxonomy-term.html.twig. See drupal_render() and
- * theme() documentation respectively for details.
+ * _theme() documentation respectively for details.
  *
  * @param $build
  *   A renderable array representing the taxonomy term content.
diff --git a/core/modules/update/update.manager.inc b/core/modules/update/update.manager.inc
index 19c0dc19abb9..79720e7714e5 100644
--- a/core/modules/update/update.manager.inc
+++ b/core/modules/update/update.manager.inc
@@ -178,9 +178,9 @@ function update_manager_update_form($form, $form_state = array(), $context) {
 
     if ($needs_manual) {
       // There are no checkboxes in the 'Manual updates' table so it will be
-      // rendered by theme('table'), not theme('tableselect'). Since the data
+      // rendered by _theme('table'), not _theme('tableselect'). Since the data
       // formats are incompatible, we convert now to the format expected by
-      // theme('table').
+      // _theme('table').
       unset($entry['#weight']);
       $attributes = $entry['#attributes'];
       unset($entry['#attributes']);
diff --git a/core/modules/user/user.api.php b/core/modules/user/user.api.php
index a697d94ecad4..80f4da9fc43c 100644
--- a/core/modules/user/user.api.php
+++ b/core/modules/user/user.api.php
@@ -343,7 +343,7 @@ function hook_user_view(\Drupal\user\UserInterface $account, \Drupal\Core\Entity
  * If the module wishes to act on the rendered HTML of the user rather than the
  * structured content array, it may use this hook to add a #post_render callback.
  * Alternatively, it could also implement hook_preprocess_HOOK() for
- * user.html.twig. See drupal_render() and theme() documentation
+ * user.html.twig. See drupal_render() and _theme() documentation
  * respectively for details.
  *
  * @param $build
diff --git a/core/themes/engines/twig/twig.engine b/core/themes/engines/twig/twig.engine
index 23e245c9f9f9..a3171245991f 100644
--- a/core/themes/engines/twig/twig.engine
+++ b/core/themes/engines/twig/twig.engine
@@ -35,7 +35,7 @@ function twig_init($template) {
 /**
  * Renders a Twig template.
  *
- * If the Twig debug setting is enabled, HTML comments including theme() call
+ * If the Twig debug setting is enabled, HTML comments including _theme() call
  * and template file name suggestions will surround the template markup.
  *
  * @param $template_file
@@ -56,7 +56,7 @@ function twig_render_template($template_file, $variables) {
   );
   if (settings()->get('twig_debug', FALSE)) {
     $output['debug_prefix'] .= "\n\n<!-- THEME DEBUG -->";
-    $output['debug_prefix'] .= "\n<!-- CALL: theme('{$variables['theme_hook_original']}') -->";
+    $output['debug_prefix'] .= "\n<!-- CALL: _theme('{$variables['theme_hook_original']}') -->";
     if (!empty($variables['theme_hook_suggestions'])) {
       $extension = twig_extension();
       $current_template = basename($template_file);
-- 
GitLab