From 6fbfb60478058e574c11c77b78169bf3d4df1a4f Mon Sep 17 00:00:00 2001
From: Mark Carver <mark.carver@me.com>
Date: Thu, 5 Jun 2014 10:59:46 -0500
Subject: [PATCH] Issue #2226207 by mgbellaire, lauriii, Mark Carver, Cottser,
 euphoric_mv: Make 'template' the default output option for hook_theme().

---
 core/includes/theme.inc                            | 21 ++++++++++-
 core/lib/Drupal/Core/Template/TwigEnvironment.php  | 18 +++++----
 core/lib/Drupal/Core/Theme/Registry.php            | 41 +++++++++++++-------
 core/modules/aggregator/aggregator.module          |  2 +
 core/modules/book/book.module                      |  2 +
 core/modules/field_ui/field_ui.module              |  1 +
 core/modules/file/file.module                      |  7 ++++
 core/modules/forum/forum.module                    |  1 +
 core/modules/image/image.module                    | 10 +++++
 core/modules/language/language.module              |  3 ++
 core/modules/locale/locale.module                  |  1 +
 core/modules/menu_ui/menu_ui.module                |  1 +
 core/modules/node/node.module                      |  1 +
 .../responsive_image/responsive_image.module       |  3 ++
 core/modules/simpletest/simpletest.module          |  1 +
 core/modules/system/system.module                  |  4 ++
 .../tests/modules/common_test/common_test.module   |  1 +
 .../theme_suggestions_test.module                  |  1 +
 .../tests/modules/theme_test/theme_test.module     |  6 +++
 core/modules/toolbar/toolbar.module                |  3 +-
 core/modules/update/update.module                  |  5 +++
 core/modules/user/user.module                      |  3 ++
 core/modules/views/views.module                    | 44 ++++++++++++----------
 core/modules/views_ui/views_ui.module              |  3 ++
 core/themes/engines/twig/twig.engine               | 23 ++++++-----
 25 files changed, 153 insertions(+), 53 deletions(-)

diff --git a/core/includes/theme.inc b/core/includes/theme.inc
index 7e92f95..7919094 100644
--- a/core/includes/theme.inc
+++ b/core/includes/theme.inc
@@ -2575,6 +2575,7 @@ function drupal_common_theme() {
     ),
     'mark' => array(
       'variables' => array('status' => MARK_NEW),
+      'function' => 'theme_mark',
     ),
     'item_list' => array(
       'variables' => array('items' => array(), 'title' => '', 'list_type' => 'ul', 'attributes' => array(), 'empty' => NULL),
@@ -2585,7 +2586,8 @@ function drupal_common_theme() {
       'template' => 'feed-icon',
     ),
     'more_link' => array(
-      'variables' => array('url' => NULL, 'title' => NULL)
+      'variables' => array('url' => NULL, 'title' => NULL),
+      'function' => 'theme_more_link',
     ),
     'progress_bar' => array(
       'variables' => array('label' => NULL, 'percent' => NULL, 'message' => NULL),
@@ -2593,6 +2595,7 @@ function drupal_common_theme() {
     ),
     'indentation' => array(
       'variables' => array('size' => 1),
+      'function' => 'theme_indentation',
     ),
     // From theme.maintenance.inc.
     'maintenance_page' => array(
@@ -2605,12 +2608,17 @@ function drupal_common_theme() {
     ),
     'task_list' => array(
       'variables' => array('items' => NULL, 'active' => NULL,  'variant' => NULL),
+      'function' => 'theme_task_list',
+      'path' => 'core/includes',
+      'file' => 'theme.maintenance.inc',
     ),
     'authorize_message' => array(
       'variables' => array('message' => NULL, 'success' => TRUE),
+      'function' => 'theme_authorize_message',
     ),
     'authorize_report' => array(
       'variables' => array('messages' => array()),
+      'function' => 'theme_authorize_report',
     ),
     // From pager.inc.
     'pager' => array(
@@ -2620,22 +2628,30 @@ function drupal_common_theme() {
     // From menu.inc.
     'menu_link' => array(
       'render element' => 'element',
+      'function' => 'theme_menu_link',
+      'path' => 'core/includes',
+      'file' => 'menu.inc',
     ),
     'menu_tree' => array(
       'render element' => 'tree',
+      'function' => 'theme_menu_tree',
     ),
     'menu_local_task' => array(
       'render element' => 'element',
+      'function' => 'theme_menu_local_task',
     ),
     'menu_local_action' => array(
       'render element' => 'element',
+      'function' => 'theme_menu_local_action',
     ),
     'menu_local_tasks' => array(
       'variables' => array('primary' => array(), 'secondary' => array()),
+      'function' => 'theme_menu_local_tasks',
     ),
     // From form.inc.
     'input' => array(
       'render element' => 'element',
+      'function' => 'theme_input',
     ),
     'select' => array(
       'render element' => 'element',
@@ -2655,6 +2671,7 @@ function drupal_common_theme() {
     ),
     'date' => array(
       'render element' => 'element',
+      'function' => 'theme_date',
     ),
     'checkboxes' => array(
       'render element' => 'element',
@@ -2670,6 +2687,7 @@ function drupal_common_theme() {
     ),
     'tableselect' => array(
       'render element' => 'element',
+      'function' => 'theme_tableselect',
     ),
     'form_element' => array(
       'render element' => 'element',
@@ -2677,6 +2695,7 @@ function drupal_common_theme() {
     ),
     'form_element_label' => array(
       'render element' => 'element',
+      'function' => 'theme_form_element_label',
     ),
     'vertical_tabs' => array(
       'render element' => 'element',
diff --git a/core/lib/Drupal/Core/Template/TwigEnvironment.php b/core/lib/Drupal/Core/Template/TwigEnvironment.php
index c671899..ae7179a 100644
--- a/core/lib/Drupal/Core/Template/TwigEnvironment.php
+++ b/core/lib/Drupal/Core/Template/TwigEnvironment.php
@@ -40,17 +40,19 @@ public function __construct(\Twig_LoaderInterface $loader = NULL, $options = arr
 
     // Set twig path namespace for themes and modules.
     $namespaces = array();
-    foreach ($module_handler->getModuleList() as $name => $extension) {
-      $namespaces[$name] = $extension->getPath();
+    foreach ($module_handler->getModuleList() as $extension) {
+      $namespaces[] = $extension->getPath();
     }
-    foreach ($theme_handler->listInfo() as $name => $extension) {
-      $namespaces[$name] = $extension->getPath();
+    foreach ($theme_handler->listInfo() as $extension) {
+      $namespaces[] = $extension->getPath();
     }
 
-    foreach ($namespaces as $name => $path) {
-      $templatesDirectory = $path . '/templates';
-      if (file_exists($templatesDirectory)) {
-        $loader->addPath($templatesDirectory, $name);
+    foreach ($namespaces as $path) {
+      $templates_dir = DRUPAL_ROOT . '/' . $path . '/templates';
+      if (file_exists($templates_dir)) {
+        // We currently do not use namespaces in Twig. We must add all possible
+        // template directory paths to the __main__ namespace.
+        $loader->addPath($templates_dir);
       }
     }
 
diff --git a/core/lib/Drupal/Core/Theme/Registry.php b/core/lib/Drupal/Core/Theme/Registry.php
index 402c9f7..abaad13 100644
--- a/core/lib/Drupal/Core/Theme/Registry.php
+++ b/core/lib/Drupal/Core/Theme/Registry.php
@@ -389,6 +389,9 @@ protected function build() {
    * @see _theme()
    * @see hook_theme()
    * @see list_themes()
+   * @see twig_render_template()
+   *
+   * @throws \BadFunctionCallException
    */
   protected function processExtension(&$cache, $name, $type, $theme, $path) {
     $result = array();
@@ -418,12 +421,6 @@ protected function processExtension(&$cache, $name, $type, $theme, $path) {
         $result[$hook]['type'] = $type;
         $result[$hook]['theme path'] = $path;
 
-        // If function and file are omitted, default to standard naming
-        // conventions.
-        if (!isset($info['template']) && !isset($info['function'])) {
-          $result[$hook]['function'] = ($type == 'module' ? 'theme_' : $name . '_') . $hook;
-        }
-
         if (isset($cache[$hook]['includes'])) {
           $result[$hook]['includes'] = $cache[$hook]['includes'];
         }
@@ -439,20 +436,36 @@ protected function processExtension(&$cache, $name, $type, $theme, $path) {
           $result[$hook]['includes'][] = $include_file;
         }
 
+        // Templates are the default implementation for a theme hook. If a
+        // theme hook provides a theme function callback, it should only
+        // override existing template implementations.
+        if (isset($info['function'])) {
+          // Ensure theming function exists and remove any provided template.
+          if (function_exists($info['function'])) {
+            unset($result[$hook]['template']);
+          }
+          else {
+            throw new \BadFunctionCallException(sprintf(
+              'Theme hook "%s" refers to a theme function callback that does not exist: "%s"',
+              $hook,
+              $info['function']
+            ));
+          }
+        }
+        // Provide a default naming convention for 'template' based on the
+        // hook used. If the template does not exist, the template engine used
+        // (Twig) will throw throw an exception at runtime when attempting to
+        // include the template file (\Twig_Error_Loader).
+        elseif (!isset($info['template'])) {
+          $result[$hook]['template'] = strtr($hook, '_', '-');
+        }
+
         // If the default keys are not set, use the default values registered
         // by the module.
         if (isset($cache[$hook])) {
           $result[$hook] += array_intersect_key($cache[$hook], $hook_defaults);
         }
 
-        // The following apply only to theming hooks implemented as templates.
-        if (isset($info['template'])) {
-          // Prepend the current theming path when none is set.
-          if (!isset($info['path'])) {
-            $result[$hook]['template'] = $path . '/templates/' . $info['template'];
-          }
-        }
-
         // Preprocess variables for all theming hooks, whether the hook is
         // implemented as a template or as a function. Ensure they are arrays.
         if (!isset($info['preprocess functions']) || !is_array($info['preprocess functions'])) {
diff --git a/core/modules/aggregator/aggregator.module b/core/modules/aggregator/aggregator.module
index 1fb5027..126fa10 100644
--- a/core/modules/aggregator/aggregator.module
+++ b/core/modules/aggregator/aggregator.module
@@ -84,10 +84,12 @@ function aggregator_theme() {
     'aggregator_page_opml' => array(
       'variables' => array('feeds' => NULL),
       'file' => 'aggregator.theme.inc',
+      'function' => 'theme_aggregator_page_opml',
     ),
     'aggregator_page_rss' => array(
       'variables' => array('feeds' => NULL),
       'file' => 'aggregator.theme.inc',
+      'function' => 'theme_aggregator_page_rss',
     ),
   );
 }
diff --git a/core/modules/book/book.module b/core/modules/book/book.module
index 9cc4835..23de3f5 100644
--- a/core/modules/book/book.module
+++ b/core/modules/book/book.module
@@ -60,6 +60,7 @@ function book_theme() {
     ),
     'book_link' => array(
       'render element' => 'element',
+      'function' => 'theme_book_link',
     ),
     'book_export_html' => array(
       'variables' => array('title' => NULL, 'contents' => NULL, 'depth' => NULL),
@@ -68,6 +69,7 @@ function book_theme() {
     'book_admin_table' => array(
       'render element' => 'form',
       'file' => 'book.admin.inc',
+      'function' => 'theme_book_admin_table',
     ),
     'book_all_books_block' => array(
       'render element' => 'book_menus',
diff --git a/core/modules/field_ui/field_ui.module b/core/modules/field_ui/field_ui.module
index 84078a2..e152755 100644
--- a/core/modules/field_ui/field_ui.module
+++ b/core/modules/field_ui/field_ui.module
@@ -87,6 +87,7 @@ function field_ui_theme() {
   return array(
     'field_ui_table' => array(
       'render element' => 'elements',
+      'function' => 'theme_field_ui_table',
     ),
   );
 }
diff --git a/core/modules/file/file.module b/core/modules/file/file.module
index 47b72ef..340a70a 100644
--- a/core/modules/file/file.module
+++ b/core/modules/file/file.module
@@ -589,26 +589,33 @@ function file_theme() {
     // file.module.
     'file_link' => array(
       'variables' => array('file' => NULL, 'icon_directory' => NULL, 'description' => NULL, 'attributes' => array()),
+      'function' => 'theme_file_link',
     ),
     'file_icon' => array(
       'variables' => array('file' => NULL, 'icon_directory' => NULL),
+      'function' => 'theme_file_icon',
     ),
     'file_managed_file' => array(
       'render element' => 'element',
+      'function' => 'theme_file_managed_file',
     ),
 
     // file.field.inc.
     'file_widget' => array(
       'render element' => 'element',
+      'function' => 'theme_file_widget',
     ),
     'file_widget_multiple' => array(
       'render element' => 'element',
+      'function' => 'theme_file_widget_multiple',
     ),
     'file_formatter_table' => array(
       'variables' => array('items' => NULL),
+      'function' => 'theme_file_formatter_table',
     ),
     'file_upload_help' => array(
       'variables' => array('description' => NULL, 'upload_validators' => NULL, 'cardinality' => NULL),
+      'function' => 'theme_file_upload_help',
     ),
   );
 }
diff --git a/core/modules/forum/forum.module b/core/modules/forum/forum.module
index 1b9b8f8..af44304 100644
--- a/core/modules/forum/forum.module
+++ b/core/modules/forum/forum.module
@@ -97,6 +97,7 @@ function forum_theme() {
     ),
     'forum_form' => array(
       'render element' => 'form',
+      'function' => 'theme_forum_form',
     ),
   );
 }
diff --git a/core/modules/image/image.module b/core/modules/image/image.module
index fc6f319..409bc58 100644
--- a/core/modules/image/image.module
+++ b/core/modules/image/image.module
@@ -108,46 +108,56 @@ function image_theme() {
         'title' => NULL,
         'attributes' => array(),
       ),
+      'function' => 'theme_image_style',
     ),
 
     // Theme functions in image.admin.inc.
     'image_style_effects' => array(
       'render element' => 'form',
       'file' => 'image.admin.inc',
+      'function' => 'theme_image_style_effects',
     ),
     'image_style_preview' => array(
       'variables' => array('style' => NULL),
       'file' => 'image.admin.inc',
+      'function' => 'theme_image_style_preview',
     ),
     'image_anchor' => array(
       'render element' => 'element',
       'file' => 'image.admin.inc',
+      'function' => 'theme_image_anchor',
     ),
     'image_resize_summary' => array(
       'variables' => array('data' => NULL),
       'file' => 'image.admin.inc',
+      'function' => 'theme_image_resize_summary',
     ),
     'image_scale_summary' => array(
       'variables' => array('data' => NULL),
       'file' => 'image.admin.inc',
+      'function' => 'theme_image_scale_summary',
     ),
     'image_crop_summary' => array(
       'variables' => array('data' => NULL),
       'file' => 'image.admin.inc',
+      'function' => 'theme_image_crop_summary',
     ),
     'image_rotate_summary' => array(
       'variables' => array('data' => NULL),
       'file' => 'image.admin.inc',
+      'function' => 'theme_image_rotate_summary',
     ),
 
     // Theme functions in image.field.inc.
     'image_widget' => array(
       'render element' => 'element',
       'file' => 'image.field.inc',
+      'function' => 'theme_image_widget',
     ),
     'image_formatter' => array(
       'variables' => array('item' => NULL, 'item_attributes' => NULL, 'path' => NULL, 'image_style' => NULL),
       'file' => 'image.field.inc',
+      'function' => 'theme_image_formatter',
     ),
   );
 }
diff --git a/core/modules/language/language.module b/core/modules/language/language.module
index 3e7a04f..1a25dc9 100644
--- a/core/modules/language/language.module
+++ b/core/modules/language/language.module
@@ -118,14 +118,17 @@ function language_theme() {
     'language_negotiation_configure_form' => array(
       'render element' => 'form',
       'file' => 'language.admin.inc',
+      'function' => 'theme_language_negotiation_configure_form',
     ),
     'language_negotiation_configure_browser_form_table' => array(
       'render element' => 'form',
       'file' => 'language.admin.inc',
+      'function' => 'theme_language_negotiation_configure_browser_form_table',
     ),
     'language_content_settings_table' => array(
       'render element' => 'element',
       'file' => 'language.admin.inc',
+      'function' => 'theme_language_content_settings_table',
     ),
   );
 }
diff --git a/core/modules/locale/locale.module b/core/modules/locale/locale.module
index d3e05dc..4c74d2c 100644
--- a/core/modules/locale/locale.module
+++ b/core/modules/locale/locale.module
@@ -190,6 +190,7 @@ function locale_theme() {
     'locale_translate_edit_form_strings' => array(
       'render element' => 'form',
       'file' => 'locale.pages.inc',
+      'function' => 'theme_locale_translate_edit_form_strings',
     ),
     'locale_translation_last_check' => array(
       'variables' => array('last' => NULL),
diff --git a/core/modules/menu_ui/menu_ui.module b/core/modules/menu_ui/menu_ui.module
index 3639d26..57ecc30 100644
--- a/core/modules/menu_ui/menu_ui.module
+++ b/core/modules/menu_ui/menu_ui.module
@@ -109,6 +109,7 @@ function menu_ui_theme() {
     'menu_overview_form' => array(
       'file' => 'menu_ui.admin.inc',
       'render element' => 'form',
+      'function' => 'theme_menu_overview_form',
     ),
   );
 }
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index 38e047b..43b7ebf 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -164,6 +164,7 @@ function node_theme() {
     ),
     'node_search_admin' => array(
       'render element' => 'form',
+      'function' => 'theme_node_search_admin',
     ),
     'node_add_list' => array(
       'variables' => array('content' => NULL),
diff --git a/core/modules/responsive_image/responsive_image.module b/core/modules/responsive_image/responsive_image.module
index 53c182c..dbb818c 100644
--- a/core/modules/responsive_image/responsive_image.module
+++ b/core/modules/responsive_image/responsive_image.module
@@ -86,6 +86,7 @@ function responsive_image_theme() {
         'attributes' => array(),
         'breakpoints' => array(),
       ),
+      'function' => 'theme_responsive_image',
     ),
     'responsive_image_formatter' => array(
       'variables' => array(
@@ -94,6 +95,7 @@ function responsive_image_theme() {
         'image_style' => NULL,
         'breakpoints' => array(),
       ),
+      'function' => 'theme_responsive_image_formatter',
     ),
     'responsive_image_source' => array(
       'variables' => array(
@@ -102,6 +104,7 @@ function responsive_image_theme() {
         'dimensions' => NULL,
         'media' => NULL,
       ),
+      'function' => 'theme_responsive_image_source',
     ),
   );
 }
diff --git a/core/modules/simpletest/simpletest.module b/core/modules/simpletest/simpletest.module
index bec96c4..c65782f 100644
--- a/core/modules/simpletest/simpletest.module
+++ b/core/modules/simpletest/simpletest.module
@@ -56,6 +56,7 @@ function simpletest_theme() {
     'simpletest_result_summary' => array(
       'render element' => 'form',
       'file' => 'simpletest.theme.inc',
+      'function' => 'theme_simpletest_result_summary',
     ),
   );
 }
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 6bd8dd4..145bc95 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -176,6 +176,7 @@ function system_theme() {
     ),
     'system_config_form' => array(
       'render element' => 'form',
+      'function' => 'theme_system_config_form',
     ),
     'confirm_form' => array(
       'render element' => 'form',
@@ -184,10 +185,12 @@ function system_theme() {
     'system_modules_details' => array(
       'render element' => 'form',
       'file' => 'system.admin.inc',
+      'function' => 'theme_system_modules_details',
     ),
     'system_modules_uninstall' => array(
       'render element' => 'form',
       'file' => 'system.admin.inc',
+      'function' => 'theme_system_modules_uninstall',
     ),
     'status_report' => array(
       'variables' => array('requirements' => NULL),
@@ -216,6 +219,7 @@ function system_theme() {
     ),
     'system_compact_link' => array(
       'variables' => array(),
+      'function' => 'theme_system_compact_link',
     ),
   ));
 }
diff --git a/core/modules/system/tests/modules/common_test/common_test.module b/core/modules/system/tests/modules/common_test/common_test.module
index 31ad453..32b5b26 100644
--- a/core/modules/system/tests/modules/common_test/common_test.module
+++ b/core/modules/system/tests/modules/common_test/common_test.module
@@ -119,6 +119,7 @@ function common_test_theme() {
     ),
     'common_test_empty' => array(
       'variables' => array('foo' => 'foo'),
+      'function' => 'theme_common_test_empty',
     ),
   );
 }
diff --git a/core/modules/system/tests/modules/theme_suggestions_test/theme_suggestions_test.module b/core/modules/system/tests/modules/theme_suggestions_test/theme_suggestions_test.module
index e963c2f..974856a 100644
--- a/core/modules/system/tests/modules/theme_suggestions_test/theme_suggestions_test.module
+++ b/core/modules/system/tests/modules/theme_suggestions_test/theme_suggestions_test.module
@@ -12,6 +12,7 @@ function theme_suggestions_test_theme() {
   $items['theme_suggestions_test_include'] = array(
     'file' => 'theme_suggestions_test.inc',
     'variables' => array(),
+    'function' => 'theme_theme_suggestions_test_include',
   );
   return $items;
 }
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 b6f13c8..27a0136 100644
--- a/core/modules/system/tests/modules/theme_test/theme_test.module
+++ b/core/modules/system/tests/modules/theme_test/theme_test.module
@@ -7,6 +7,7 @@ function theme_test_theme($existing, $type, $theme, $path) {
   $items['theme_test'] = array(
     'file' => 'theme_test.inc',
     'variables' => array('foo' => ''),
+    'function' => 'theme_theme_test',
   );
   $items['theme_test_template_test'] = array(
     'template' => 'theme_test.template_test',
@@ -32,12 +33,15 @@ function theme_test_theme($existing, $type, $theme, $path) {
   );
   $items['theme_test_function_suggestions'] = array(
     'variables' => array(),
+    'function' => 'theme_theme_test_function_suggestions',
   );
   $items['theme_test_suggestions_include'] = array(
     'variables' => array(),
+    'function' => 'theme_theme_test_suggestions_include',
   );
   $items['theme_test_foo'] = array(
     'variables' => array('foo' => NULL),
+    'function' => 'theme_theme_test_foo',
   );
   $items['theme_test_render_element'] = array(
     'render element' => 'elements',
@@ -45,9 +49,11 @@ function theme_test_theme($existing, $type, $theme, $path) {
   );
   $items['theme_test_render_element_children'] = array(
     'render element' => 'element',
+    'function' => 'theme_theme_test_render_element_children',
   );
   $items['theme_test_function_template_override'] = array(
     'variables' => array(),
+    'function' => 'theme_theme_test_function_template_override',
   );
   $info['test_theme_not_existing_function'] = array(
     'function' => 'test_theme_not_existing_function',
diff --git a/core/modules/toolbar/toolbar.module b/core/modules/toolbar/toolbar.module
index f46770a..402abc1 100644
--- a/core/modules/toolbar/toolbar.module
+++ b/core/modules/toolbar/toolbar.module
@@ -50,7 +50,6 @@ function toolbar_permission() {
 function toolbar_theme($existing, $type, $theme, $path) {
   $items['toolbar'] = array(
     'render element' => 'element',
-    'template' => 'toolbar',
   );
   $items['toolbar_item'] = array(
     'render element' => 'element',
@@ -95,7 +94,7 @@ function toolbar_element_info() {
   // property contains a renderable array.
   $elements['toolbar_item'] = array(
     '#pre_render' => array('toolbar_pre_render_item'),
-    '#theme' => 'toolbar_item',
+    '#theme' => 'theme_toolbar_item',
     'tab' => array(
       '#type' => 'link',
       '#title' => NULL,
diff --git a/core/modules/update/update.module b/core/modules/update/update.module
index eb77313..93af25d 100644
--- a/core/modules/update/update.module
+++ b/core/modules/update/update.module
@@ -175,21 +175,26 @@ function update_theme() {
     'update_manager_update_form' => array(
       'render element' => 'form',
       'file' => 'update.manager.inc',
+      'function' => 'theme_update_manager_update_form',
     ),
     'update_last_check' => array(
       'variables' => array('last' => NULL),
+      'function' => 'theme_update_last_check',
     ),
     'update_report' => array(
       'variables' => array('data' => NULL),
       'file' => 'update.report.inc',
+      'function' => 'theme_update_report',
     ),
     'update_version' => array(
       'variables' => array('version' => NULL, 'tag' => NULL, 'class' => array()),
       'file' => 'update.report.inc',
+      'function' => 'theme_update_version',
     ),
     'update_status_label' => array(
       'variables' => array('status' => NULL),
       'file' => 'update.report.inc',
+      'function' => 'theme_update_status_label',
     ),
   );
 }
diff --git a/core/modules/user/user.module b/core/modules/user/user.module
index 4b28c81..9dea4f2 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -109,12 +109,15 @@ function user_theme() {
     'user_permission_description' => array(
       'variables' => array('permission_item' => NULL, 'hide' => NULL),
       'file' => 'user.admin.inc',
+      'function' => 'theme_user_permission_description',
     ),
     'user_signature' => array(
       'variables' => array('signature' => NULL),
+      'function' => 'theme_user_signature',
     ),
     'username' => array(
       'variables' => array('account' => NULL, 'attributes' => array()),
+      'function' => 'theme_username',
     ),
   );
 }
diff --git a/core/modules/views/views.module b/core/modules/views/views.module
index 216bc93..8fe8ebb 100644
--- a/core/modules/views/views.module
+++ b/core/modules/views/views.module
@@ -121,7 +121,6 @@ function views_theme($existing, $type, $theme, $path) {
   // Our extra version of pager from pager.inc
   $hooks['views_mini_pager'] = $base + array(
     'variables' => array('tags' => array(), 'quantity' => 10, 'element' => 0, 'parameters' => array()),
-    'template' => 'views-mini-pager',
   );
 
   $variables = array(
@@ -141,10 +140,10 @@ function views_theme($existing, $type, $theme, $path) {
   // Default view themes
   $hooks['views_view_field'] = $base + array(
     'variables' => array('view' => NULL, 'field' => NULL, 'row' => NULL),
+    'function' => 'theme_views_view_field',
   );
   $hooks['views_view_grouping'] = $base + array(
     'variables' => array('view' => NULL, 'grouping' => NULL, 'grouping_level' => NULL, 'rows' => NULL, 'title' => NULL),
-    'template' => 'views-view-grouping',
   );
 
   $plugins = Views::getPluginDefinitions();
@@ -173,35 +172,44 @@ function views_theme($existing, $type, $theme, $path) {
         'variables' => $variables[$type],
       );
 
-      // For the views module we ensure views.theme.inc is included.
-      if ($def['provider'] == 'views') {
-        $def['theme_file'] = 'views.theme.inc';
-      }
       // We always use the module directory as base dir.
       $module_dir = drupal_get_path('module', $def['provider']);
+      $hooks[$def['theme']]['path'] = $module_dir;
 
+      // For the views module we ensure views.theme.inc is included.
+      if ($def['provider'] == 'views') {
+        if (!isset($hooks[$def['theme']]['includes'])) {
+          $hooks[$def['theme']]['includes'] = array();
+        }
+        if (!in_array('views.theme.inc', $hooks[$def['theme']]['includes'])) {
+          $hooks[$def['theme']]['includes'][] = $module_dir . '/views.theme.inc';
+        }
+      }
       // The theme_file definition is always relative to the modules directory.
-      if (isset($def['theme_file'])) {
-        $hooks[$def['theme']]['path'] = $module_dir;
+      elseif (!empty($def['theme_file'])) {
         $hooks[$def['theme']]['file'] = $def['theme_file'];
       }
+
       // Whenever we got a theme file, we include it directly so we can
       // auto-detect the theme function.
       if (isset($def['theme_file'])) {
-        $include = DRUPAL_ROOT . '/' . $module_dir. '/' . $def['theme_file'];
+        $include = DRUPAL_ROOT . '/' . $module_dir . '/' . $def['theme_file'];
         if (is_file($include)) {
           require_once $include;
         }
       }
-     // If there is no theme function for the given theme definition, we assume
-     // a template file shall be used. By default this file is located in the
-     // /templates directory of the module's folder.
-     // If a module wants to define its own location it has to set
-     // register_theme of the plugin to FALSE and implement hook_theme() by
-     // itself.
+
+      // If there is no theme function for the given theme definition, it must
+      // be a template file. By default this file is located in the /templates
+      // directory of the module's folder. If a module wants to define its own
+      // location it has to set register_theme of the plugin to FALSE and
+      // implement hook_theme() by itself.
       if (!function_exists('theme_' . $def['theme'])) {
-        $hooks[$def['theme']]['path'] = $module_dir;
-        $hooks[$def['theme']]['template'] = 'templates/' . drupal_clean_css_identifier($def['theme']);
+        $hooks[$def['theme']]['path'] .= '/templates';
+        $hooks[$def['theme']]['template'] = drupal_clean_css_identifier($def['theme']);
+      }
+      else {
+        $hooks[$def['theme']]['function'] = 'theme_' . $def['theme'];
       }
     }
   }
@@ -211,13 +219,11 @@ function views_theme($existing, $type, $theme, $path) {
   );
 
   $hooks['views_exposed_form'] = $base + array(
-    'template' => 'views-exposed-form',
     'render element' => 'form',
   );
 
   $hooks['views_more'] = $base + array(
     'variables' => array('more_url' => NULL, 'link_text' => 'more', 'view' => NULL),
-    'template' => 'views-more',
   );
 
   return $hooks;
diff --git a/core/modules/views_ui/views_ui.module b/core/modules/views_ui/views_ui.module
index ba5e342..dc5e7cc 100644
--- a/core/modules/views_ui/views_ui.module
+++ b/core/modules/views_ui/views_ui.module
@@ -80,10 +80,12 @@ function views_ui_theme() {
     'views_ui_rearrange_filter_form' => array(
       'render element' => 'form',
       'file' => 'views_ui.theme.inc',
+      'function' => 'theme_views_ui_rearrange_filter_form',
     ),
     'views_ui_expose_filter_form' => array(
       'render element' => 'form',
       'file' => 'views_ui.theme.inc',
+      'function' => 'theme_views_ui_expose_filter_form',
     ),
 
     // list views
@@ -97,6 +99,7 @@ function views_ui_theme() {
     'views_ui_build_group_filter_form' => array(
       'render element' => 'form',
       'file' => 'views_ui.theme.inc',
+      'function' => 'theme_views_ui_build_group_filter_form',
     ),
 
     // On behalf of a plugin
diff --git a/core/themes/engines/twig/twig.engine b/core/themes/engines/twig/twig.engine
index 1595bf8..b6d8fdb 100644
--- a/core/themes/engines/twig/twig.engine
+++ b/core/themes/engines/twig/twig.engine
@@ -36,22 +36,27 @@ function twig_init(Extension $theme) {
  * 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
+ * @param string $template_file
  *   The file name of the template to render.
- * @param $variables
+ * @param array $variables
  *   A keyed array of variables that will appear in the output.
  *
- * @return
+ * @return string
  *   The output generated by the template, plus any debug information.
  */
 function twig_render_template($template_file, $variables) {
   $twig_service = \Drupal::service('twig');
-  $output = array(
-    'debug_prefix'    => '',
-    'debug_info'      => '',
-    'rendered_markup' => $twig_service->loadTemplate($template_file)->render($variables),
-    'debug_suffix'    => '',
-  );
+  try {
+    $output = array(
+      'debug_prefix'    => '',
+      'debug_info'      => '',
+      'rendered_markup' => $twig_service->loadTemplate($template_file)->render($variables),
+      'debug_suffix'    => '',
+    );
+  }
+  catch (\Twig_Error_Loader $e) {
+    drupal_set_message($e->getMessage(), 'error');
+  }
   if ($twig_service->isDebug()) {
     $output['debug_prefix'] .= "\n\n<!-- THEME DEBUG -->";
     $output['debug_prefix'] .= "\n<!-- CALL: _theme('{$variables['theme_hook_original']}') -->";
-- 
1.9.1

