diff --git a/core/lib/Drupal/Component/Annotation/AnnotationInterface.php b/core/lib/Drupal/Component/Annotation/AnnotationInterface.php
index 1168510..bbb86cd 100644
--- a/core/lib/Drupal/Component/Annotation/AnnotationInterface.php
+++ b/core/lib/Drupal/Component/Annotation/AnnotationInterface.php
@@ -20,9 +20,9 @@ public function get();
   public function getProvider();
 
   /**
-   * Sets the name of the provider of the annotated class.
+   * Sets the name of the provider(s) of the annotated class.
    *
-   * @param string $provider
+   * @param array|string $provider
    */
   public function setProvider($provider);
 
diff --git a/core/lib/Drupal/Component/Annotation/Plugin.php b/core/lib/Drupal/Component/Annotation/Plugin.php
index 29f3675..4df1f3c 100644
--- a/core/lib/Drupal/Component/Annotation/Plugin.php
+++ b/core/lib/Drupal/Component/Annotation/Plugin.php
@@ -78,7 +78,10 @@ public function get() {
    * {@inheritdoc}
    */
   public function getProvider() {
-    return isset($this->definition['provider']) ? $this->definition['provider'] : FALSE;
+    if (isset($this->definition['provider'])) {
+      return is_array($this->definition['provider']) ? reset($this->definition['provider']) : $this->definition['provider'];
+    }
+    return FALSE;
   }
 
   /**
diff --git a/core/lib/Drupal/Component/Annotation/Plugin/Discovery/AnnotatedClassDiscovery.php b/core/lib/Drupal/Component/Annotation/Plugin/Discovery/AnnotatedClassDiscovery.php
index e564fd0..a35b22c 100644
--- a/core/lib/Drupal/Component/Annotation/Plugin/Discovery/AnnotatedClassDiscovery.php
+++ b/core/lib/Drupal/Component/Annotation/Plugin/Discovery/AnnotatedClassDiscovery.php
@@ -19,6 +19,11 @@ class AnnotatedClassDiscovery implements DiscoveryInterface {
   use DiscoveryTrait;
 
   /**
+   * Whether the parser should return only class annotations.
+   */
+  const CLASS_ANNOTATION_OPTIMIZE = TRUE;
+
+  /**
    * The namespaces within which to find plugin classes.
    *
    * @var string[]
@@ -138,11 +143,11 @@ public function getDefinitions() {
               // file. However, StaticReflectionParser needs a finder, so use a
               // mock version.
               $finder = MockFileFinder::create($fileinfo->getPathName());
-              $parser = new StaticReflectionParser($class, $finder, TRUE);
+              $parser = new StaticReflectionParser($class, $finder, static::CLASS_ANNOTATION_OPTIMIZE);
 
               /** @var $annotation \Drupal\Component\Annotation\AnnotationInterface */
               if ($annotation = $reader->getClassAnnotation($parser->getReflectionClass(), $this->pluginDefinitionAnnotationName)) {
-                $this->prepareAnnotationDefinition($annotation, $class);
+                $this->prepareAnnotationDefinition($annotation, $class, $parser);
 
                 $id = $annotation->getId();
                 $content = $annotation->get();
@@ -173,8 +178,10 @@ public function getDefinitions() {
    *   The annotation derived from the plugin.
    * @param string $class
    *   The class used for the plugin.
+   * @param \Doctrine\Common\Reflection\StaticReflectionParser $parser
+   *   The static reflection parser.
    */
-  protected function prepareAnnotationDefinition(AnnotationInterface $annotation, $class) {
+  protected function prepareAnnotationDefinition(AnnotationInterface $annotation, $class, StaticReflectionParser $parser) {
     $annotation->setClass($class);
   }
 
diff --git a/core/lib/Drupal/Component/Plugin/Discovery/StaticReflectionParser.php b/core/lib/Drupal/Component/Plugin/Discovery/StaticReflectionParser.php
new file mode 100644
index 0000000..05aad55
--- /dev/null
+++ b/core/lib/Drupal/Component/Plugin/Discovery/StaticReflectionParser.php
@@ -0,0 +1,32 @@
+<?php
+
+namespace Drupal\Component\Plugin\Discovery;
+
+use Doctrine\Common\Reflection\StaticReflectionParser as BaseStaticReflectionParser;
+
+/**
+ * Allows getting the reflection parser for the parent class.
+ */
+class StaticReflectionParser extends BaseStaticReflectionParser {
+
+  /**
+   * If the current class extends another, get the parser for the latter.
+   *
+   * @param \Doctrine\Common\Reflection\StaticReflectionParser $parser
+   *   The current static parser.
+   * @param $finder
+   *   The class finder. Must implement
+   *  \Doctrine\Common\Reflection\ClassFinderInterface, but can do so
+   *  implicitly (i.e., implements the interface's methods but not the actual
+   *  interface).
+   *
+   * @return static|null
+   *   The static parser for the parent if there's a parent class or NULL.
+   */
+  public static function getParentParser(BaseStaticReflectionParser $parser, $finder) {
+    if ($parser->parentClassName) {
+      return new static($parser->parentClassName, $finder, $parser->classAnnotationOptimize);
+    }
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php b/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php
index fa706a4..a84734f 100644
--- a/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php
+++ b/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php
@@ -14,6 +14,7 @@
 use Drupal\Core\Cache\Cache;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
+use Drupal\Core\Plugin\Discovery\ProviderFilterDecorator;
 use Drupal\Core\Plugin\Factory\ContainerFactory;
 
 /**
@@ -288,19 +289,9 @@ protected function findDefinitions() {
       $this->processDefinition($definition, $plugin_id);
     }
     $this->alterDefinitions($definitions);
-    // If this plugin was provided by a module that does not exist, remove the
-    // plugin definition.
-    foreach ($definitions as $plugin_id => $plugin_definition) {
-      // If the plugin definition is an object, attempt to convert it to an
-      // array, if that is not possible, skip further processing.
-      if (is_object($plugin_definition) && !($plugin_definition = (array) $plugin_definition)) {
-        continue;
-      }
-      if (isset($plugin_definition['provider']) && !in_array($plugin_definition['provider'], array('core', 'component')) && !$this->providerExists($plugin_definition['provider'])) {
-        unset($definitions[$plugin_id]);
-      }
-    }
-    return $definitions;
+    return ProviderFilterDecorator::filterDefinitions($definitions, function ($provider) {
+      return $this->providerExists($provider);
+    });
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Plugin/Discovery/AnnotatedClassDiscovery.php b/core/lib/Drupal/Core/Plugin/Discovery/AnnotatedClassDiscovery.php
index 770308d..f49dced 100644
--- a/core/lib/Drupal/Core/Plugin/Discovery/AnnotatedClassDiscovery.php
+++ b/core/lib/Drupal/Core/Plugin/Discovery/AnnotatedClassDiscovery.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\Core\Plugin\Discovery;
 
+use Doctrine\Common\Reflection\StaticReflectionParser;
 use Drupal\Component\Annotation\AnnotationInterface;
 use Drupal\Component\Annotation\Plugin\Discovery\AnnotatedClassDiscovery as ComponentAnnotatedClassDiscovery;
 use Drupal\Component\Utility\Unicode;
@@ -82,8 +83,8 @@ protected function getAnnotationReader() {
   /**
    * {@inheritdoc}
    */
-  protected function prepareAnnotationDefinition(AnnotationInterface $annotation, $class) {
-    parent::prepareAnnotationDefinition($annotation, $class);
+  protected function prepareAnnotationDefinition(AnnotationInterface $annotation, $class, StaticReflectionParser $parser) {
+    parent::prepareAnnotationDefinition($annotation, $class, $parser);
 
     if (!$annotation->getProvider()) {
       $annotation->setProvider($this->getProviderFromNamespace($class));
diff --git a/core/lib/Drupal/Core/Plugin/Discovery/AnnotatedClassDiscoveryAutomatedProviders.php b/core/lib/Drupal/Core/Plugin/Discovery/AnnotatedClassDiscoveryAutomatedProviders.php
new file mode 100644
index 0000000..6530d51
--- /dev/null
+++ b/core/lib/Drupal/Core/Plugin/Discovery/AnnotatedClassDiscoveryAutomatedProviders.php
@@ -0,0 +1,69 @@
+<?php
+
+namespace Drupal\Core\Plugin\Discovery;
+
+use Doctrine\Common\Reflection\StaticReflectionParser as BaseStaticReflectionParser;
+use Drupal\Component\Annotation\AnnotationInterface;
+use Drupal\Component\Plugin\Discovery\StaticReflectionParser;
+
+/**
+ * Adds the namespaces in the plugin class use statements as providers.
+ */
+class AnnotatedClassDiscoveryAutomatedProviders extends AnnotatedClassDiscovery {
+
+  /**
+   * {@inheritdoc}
+   */
+  const CLASS_ANNOTATION_OPTIMIZE = FALSE;
+
+  /**
+   * Any class loader with a findFile() method.
+   *
+   * @var \Composer\Autoload\ClassLoader
+   */
+  protected $finder;
+
+  /**
+   * Constructs an AnnotatedClassDiscoveryAutomatedProviders object.
+   *
+   * @param string $subdir
+   *   Either the plugin's subdirectory, for example 'Plugin/views/filter', or
+   *   empty string if plugins are located at the top level of the namespace.
+   * @param \Traversable $root_namespaces
+   *   An object that implements \Traversable which contains the root paths
+   *   keyed by the corresponding namespace to look for plugin implementations.
+   *   If $subdir is not an empty string, it will be appended to each namespace.
+   * @param string $plugin_definition_annotation_name
+   *   The name of the annotation that contains the plugin definition.
+   *   Defaults to 'Drupal\Component\Annotation\Plugin'.
+   * @param string[] $annotation_namespaces
+   *   Additional namespaces to scan for annotation definitions.
+   * @param $class_loader
+   *   The class loader already know where to find the classes so it is reused
+   *   as the class finder.
+   */
+  public function __construct($subdir, \Traversable $root_namespaces, $plugin_definition_annotation_name, array $annotation_namespaces, $class_loader) {
+    parent::__construct($subdir, $root_namespaces, $plugin_definition_annotation_name, $annotation_namespaces);
+
+    if (!method_exists($class_loader, 'findFile')) {
+      throw new \LogicException('Class loader must implement findFile() method');
+    }
+    $this->finder = $class_loader;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function prepareAnnotationDefinition(AnnotationInterface $annotation, $class, BaseStaticReflectionParser $parser) {
+    parent::prepareAnnotationDefinition($annotation, $class, $parser);
+    $providers = (array) $annotation->getProvider();
+    // Loop through all the parent classes and add their providers (which we
+    // infer by parsing their use statements) to the $providers array.
+    do {
+      $new_providers = array_map([$this, 'getProviderFromNamespace'], $parser->getUseStatements());
+      $providers = array_merge($providers, $new_providers);
+    } while ($parser = StaticReflectionParser::getParentParser($parser, $this->finder));
+    $annotation->setProvider(array_unique(array_filter($providers)));
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Plugin/Discovery/ProviderFilterDecorator.php b/core/lib/Drupal/Core/Plugin/Discovery/ProviderFilterDecorator.php
new file mode 100644
index 0000000..8d8c02d
--- /dev/null
+++ b/core/lib/Drupal/Core/Plugin/Discovery/ProviderFilterDecorator.php
@@ -0,0 +1,94 @@
+<?php
+
+namespace Drupal\Core\Plugin\Discovery;
+
+use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
+use Drupal\Component\Plugin\Discovery\DiscoveryTrait;
+
+/**
+ * Remove plugin definitions with non-existing providers.
+ */
+class ProviderFilterDecorator implements DiscoveryInterface {
+
+  use DiscoveryTrait;
+
+  /**
+   * The Discovery object being decorated.
+   *
+   * @var \Drupal\Component\Plugin\Discovery\DiscoveryInterface
+   */
+  protected $decorated;
+
+  /**
+   * A callable for testing if a provider exists.
+   *
+   * @var callable
+   */
+  protected $providerExists;
+
+  /**
+   * Constructs a InheritProviderDecorator object.
+   *
+   * @param \Drupal\Component\Plugin\Discovery\DiscoveryInterface $decorated
+   *   The object implementing DiscoveryInterface that is being decorated.
+   * @param callable $provider_exists
+   *   A callable, gets passed a provider name, should return TRUE if the
+   *   provider exists and FALSE if not.
+   */
+  public function __construct(DiscoveryInterface $decorated, callable $provider_exists) {
+    $this->decorated = $decorated;
+    $this->providerExists = $provider_exists;
+  }
+
+  /**
+   * Removes plugin definitions with non-existing providers.
+   *
+   * @param mixed[] $definitions
+   *   An array of plugin definitions (empty array if no definitions were
+   *   found). Keys are plugin IDs.
+   * @param callable $provider_exists
+   *   A callable, gets passed a provider name, should return TRUE if the
+   *   provider exists and FALSE if not.
+   *
+   * @return array|\mixed[] $definitions
+   *   An array of plugin definitions. If a definition is an array and has a
+   *   provider key that provider is guaranteed to exist.
+   */
+  public static function filterDefinitions(array $definitions, callable $provider_exists) {
+    // Besides what the caller accepts, we also accept core or component.
+    $provider_exists = function ($provider) use ($provider_exists) {
+      return in_array($provider, ['core', 'component']) || $provider_exists($provider);
+    };
+    return array_filter($definitions, function ($definition) use ($provider_exists) {
+      // Plugin definitions can be objects (for example, Typed Data) those will
+      // become empty array here and cause no problems.
+      $definition = (array) $definition + ['provider' => []];
+      // There can be one or many providers, handle them as multiple always.
+      $providers = (array) $definition['provider'];
+      return count($providers) == count(array_filter($providers, $provider_exists));
+    });
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDefinitions() {
+    return static::filterDefinitions($this->decorated->getDefinitions(), $this->providerExists);
+  }
+
+  /**
+   * Passes through all unknown calls onto the decorated object.
+   *
+   * @param string $method
+   *   The method to call on the decorated object.
+   * @param array $args
+   *   Call arguments.
+   *
+   * @return mixed
+   *   The return value from the method on the decorated object.
+   */
+  public function __call($method, array $args) {
+    return call_user_func_array([$this->decorated, $method], $args);
+  }
+
+}
diff --git a/core/modules/block_content/migration_templates/block_content_body_field.yml b/core/modules/block_content/migration_templates/block_content_body_field.yml
index 41484f8..b51d032 100644
--- a/core/modules/block_content/migration_templates/block_content_body_field.yml
+++ b/core/modules/block_content/migration_templates/block_content_body_field.yml
@@ -30,3 +30,6 @@ destination:
 migration_dependencies:
   required:
     - block_content_type
+provider:
+  - block_content
+  - migrate_drupal
diff --git a/core/modules/block_content/migration_templates/block_content_type.yml b/core/modules/block_content/migration_templates/block_content_type.yml
index 7b77ba5..bc75eea 100644
--- a/core/modules/block_content/migration_templates/block_content_type.yml
+++ b/core/modules/block_content/migration_templates/block_content_type.yml
@@ -17,3 +17,6 @@ process:
   label: label
 destination:
   plugin: entity:block_content_type
+provider:
+  - block_content
+  - migrate_drupal
diff --git a/core/modules/migrate/migrate.services.yml b/core/modules/migrate/migrate.services.yml
index da43b38..c9a6457 100644
--- a/core/modules/migrate/migrate.services.yml
+++ b/core/modules/migrate/migrate.services.yml
@@ -6,8 +6,8 @@ services:
     factory: cache_factory:get
     arguments: [migrate]
   plugin.manager.migrate.source:
-    class: Drupal\migrate\Plugin\MigratePluginManager
-    arguments: [source, '@container.namespaces', '@cache.discovery', '@module_handler', 'Drupal\migrate\Annotation\MigrateSource']
+    class: Drupal\migrate\Plugin\MigrateSourcePluginManager
+    arguments: [source, '@container.namespaces', '@cache.discovery', '@module_handler', '@class_loader']
   plugin.manager.migrate.process:
     class: Drupal\migrate\Plugin\MigratePluginManager
     arguments: [process, '@container.namespaces', '@cache.discovery', '@module_handler', 'Drupal\migrate\Annotation\MigrateProcessPlugin']
diff --git a/core/modules/migrate/src/Plugin/MigrateSourcePluginManager.php b/core/modules/migrate/src/Plugin/MigrateSourcePluginManager.php
new file mode 100644
index 0000000..267d21e
--- /dev/null
+++ b/core/modules/migrate/src/Plugin/MigrateSourcePluginManager.php
@@ -0,0 +1,62 @@
+<?php
+
+namespace Drupal\migrate\Plugin;
+
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscoveryAutomatedProviders;
+use Drupal\Core\Plugin\Discovery\ContainerDerivativeDiscoveryDecorator;
+
+/**
+ * Plugin manager for migrate source plugins.
+ *
+ * @see \Drupal\migrate\Plugin\MigrateSourceInterface
+ * @see \Drupal\migrate\Plugin\source\SourcePluginBase
+ * @see \Drupal\migrate\Annotation\MigrateSource
+ * @see plugin_api
+ *
+ * @ingroup migration
+ */
+class MigrateSourcePluginManager extends MigratePluginManager {
+
+  /**
+   * The class loader.
+   *
+   * @var object
+   */
+  protected $classLoader;
+
+  /**
+   * MigrateSourcePluginManager constructor.
+   *
+   * @param string $type
+   *   The type of the plugin: row, source, process, destination, entity_field,
+   *   id_map.
+   * @param \Traversable $namespaces
+   *   An object that implements \Traversable which contains the root paths
+   *   keyed by the corresponding namespace to look for plugin implementations.
+   * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
+   *   Cache backend instance to use.
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   The module handler to invoke the alter hook with.
+   * @param object $class_loader
+   *   The class loader.
+   */
+  public function __construct($type, \Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler, $class_loader) {
+    parent::__construct($type, $namespaces, $cache_backend, $module_handler, 'Drupal\migrate\Annotation\MigrateSource');
+    $this->classLoader = $class_loader;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getDiscovery() {
+    if (!$this->discovery) {
+      $discovery = new AnnotatedClassDiscoveryAutomatedProviders($this->subdir, $this->namespaces, $this->pluginDefinitionAnnotationName, $this->additionalAnnotationNamespaces, $this->classLoader);
+      $this->discovery = new ContainerDerivativeDiscoveryDecorator($discovery);
+    }
+    return $this->discovery;
+  }
+
+}
+
diff --git a/core/modules/migrate/src/Plugin/MigrationPluginManager.php b/core/modules/migrate/src/Plugin/MigrationPluginManager.php
index d082c11..78f9e2f 100644
--- a/core/modules/migrate/src/Plugin/MigrationPluginManager.php
+++ b/core/modules/migrate/src/Plugin/MigrationPluginManager.php
@@ -9,6 +9,7 @@
 use Drupal\Core\Language\LanguageManagerInterface;
 use Drupal\Core\Plugin\DefaultPluginManager;
 use Drupal\Core\Plugin\Discovery\ContainerDerivativeDiscoveryDecorator;
+use Drupal\Core\Plugin\Discovery\ProviderFilterDecorator;
 use Drupal\Core\Plugin\Discovery\YamlDirectoryDiscovery;
 use Drupal\Core\Plugin\Factory\ContainerFactory;
 use Drupal\migrate\MigrateBuildDependencyInterface;
@@ -68,7 +69,15 @@ protected function getDiscovery() {
       }, $this->moduleHandler->getModuleDirectories());
 
       $yaml_discovery = new YamlDirectoryDiscovery($directories, 'migrate');
-      $this->discovery = new ContainerDerivativeDiscoveryDecorator($yaml_discovery);
+      // This gets rid of migrations which try to use a non-existent source
+      // plugin. The common case for this is if the source plugin has, or
+      // specifies, a non-existent provider.
+      $only_with_source_discovery  = new NoSourcePluginDecorator($yaml_discovery);
+      // This gets rid of migrations with explicit providers set if one of the
+      // providers do not exist before we try to use a potentially non-existing
+      // deriver. This is a rare case.
+      $filtered_discovery = new ProviderFilterDecorator($only_with_source_discovery, [$this->moduleHandler, 'moduleExists']);
+      $this->discovery = new ContainerDerivativeDiscoveryDecorator($filtered_discovery);
     }
     return $this->discovery;
   }
diff --git a/core/modules/migrate/src/Plugin/NoSourcePluginDecorator.php b/core/modules/migrate/src/Plugin/NoSourcePluginDecorator.php
new file mode 100644
index 0000000..aa2121e
--- /dev/null
+++ b/core/modules/migrate/src/Plugin/NoSourcePluginDecorator.php
@@ -0,0 +1,58 @@
+<?php
+
+namespace Drupal\migrate\Plugin;
+
+use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
+use Drupal\Component\Plugin\Discovery\DiscoveryTrait;
+
+/**
+ * Remove definitions which refer to a non-existing source plugin.
+ */
+class NoSourcePluginDecorator implements DiscoveryInterface {
+
+  use DiscoveryTrait;
+
+  /**
+   * The Discovery object being decorated.
+   *
+   * @var \Drupal\Component\Plugin\Discovery\DiscoveryInterface
+   */
+  protected $decorated;
+
+  /**
+   * Constructs a NoSourcePluginDecorator object.
+   *
+   * @param \Drupal\Component\Plugin\Discovery\DiscoveryInterface $decorated
+   *   The object implementing DiscoveryInterface that is being decorated.
+   */
+  public function __construct(DiscoveryInterface $decorated) {
+    $this->decorated = $decorated;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDefinitions() {
+    /** @var \Drupal\Component\Plugin\PluginManagerInterface $source_plugin_manager */
+    $source_plugin_manager = \Drupal::service('plugin.manager.migrate.source');
+    return array_filter($this->decorated->getDefinitions(), function (array $definition) use ($source_plugin_manager) {
+      return $source_plugin_manager->hasDefinition($definition['source']['plugin']);
+    });
+  }
+
+  /**
+   * Passes through all unknown calls onto the decorated object.
+   *
+   * @param string $method
+   *   The method to call on the decorated object.
+   * @param array $args
+   *   Call arguments.
+   *
+   * @return mixed
+   *   The return value from the method on the decorated object.
+   */
+  public function __call($method, array $args) {
+    return call_user_func_array([$this->decorated, $method], $args);
+  }
+
+}
diff --git a/core/modules/migrate/tests/src/Kernel/MigrationTest.php b/core/modules/migrate/tests/src/Kernel/MigrationTest.php
index 9ff9f80..5fe56ec 100644
--- a/core/modules/migrate/tests/src/Kernel/MigrationTest.php
+++ b/core/modules/migrate/tests/src/Kernel/MigrationTest.php
@@ -34,8 +34,8 @@ public function testSetInvalidation() {
     $this->assertEqual('entity:entity_view_mode', $migration->getDestinationPlugin()->getPluginId());
 
     // Test the source plugin is invalidated.
-    $migration->set('source', ['plugin' => 'd6_field']);
-    $this->assertEqual('d6_field', $migration->getSourcePlugin()->getPluginId());
+    $migration->set('source', ['plugin' => 'embedded_data', 'data_rows' => [], 'ids' => []]);
+    $this->assertEqual('embedded_data', $migration->getSourcePlugin()->getPluginId());
 
     // Test the destination plugin is invalidated.
     $migration->set('destination', ['plugin' => 'null']);
diff --git a/core/modules/migrate/tests/src/Kernel/Plugin/MigrationPluginListTest.php b/core/modules/migrate/tests/src/Kernel/Plugin/MigrationPluginListTest.php
new file mode 100644
index 0000000..f732d9f
--- /dev/null
+++ b/core/modules/migrate/tests/src/Kernel/Plugin/MigrationPluginListTest.php
@@ -0,0 +1,106 @@
+<?php
+
+namespace Drupal\Tests\migrate\Kernel\Plugin;
+
+use Drupal\Core\Database\Database;
+use Drupal\KernelTests\KernelTestBase;
+
+/**
+ * Tests the migration plugin manager.
+ *
+ * @coversDefaultClass \Drupal\migrate\Plugin\MigratePluginManager
+ * @group migrate
+ */
+class MigrationPluginListTest extends KernelTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = [
+    'migrate',
+    // Test with all modules containing Drupal migrations.
+    'action',
+    'aggregator',
+    'ban',
+    'block',
+    'block_content',
+    'book',
+    'comment',
+    'contact',
+    'dblog',
+    'field',
+    'file',
+    'filter',
+    'forum',
+    'image',
+    'language',
+    'locale',
+    'menu_link_content',
+    'menu_ui',
+    'node',
+    'path',
+    'search',
+    'shortcut',
+    'simpletest',
+    'statistics',
+    'syslog',
+    'system',
+    'taxonomy',
+    'text',
+    'tracker',
+    'update',
+    'user',
+  ];
+
+  /**
+   * @covers ::getDefinitions
+   */
+  public function testGetDefinitions() {
+    // Make sure retrieving all the core migration plugins does not throw any
+    // errors.
+    $migration_plugins = $this->container->get('plugin.manager.migration')->getDefinitions();
+    // All the plugins provided by core depend on migrate_drupal.
+    $this->assertEmpty($migration_plugins);
+
+    // Enable a module that provides migrations that do not depend on
+    // migrate_drupal.
+    $this->enableModules(['migrate_external_translated_test']);
+    $migration_plugins = $this->container->get('plugin.manager.migration')->getDefinitions();
+    // All the plugins provided by migrate_external_translated_test do not
+    // depend on migrate_drupal.
+    $this::assertArrayHasKey('external_translated_test_node', $migration_plugins);
+    $this::assertArrayHasKey('external_translated_test_node_translation', $migration_plugins);
+
+    // Disable the test module and the list should be empty again.
+    $this->disableModules(['migrate_external_translated_test']);
+    $migration_plugins = $this->container->get('plugin.manager.migration')->getDefinitions();
+    // All the plugins provided by core depend on migrate_drupal.
+    $this->assertEmpty($migration_plugins);
+
+    // Enable migrate_drupal to test that the plugins can now be discovered.
+    $this->enableModules(['migrate_drupal']);
+    // Set up a migrate database connection so that plugin discovery works.
+    // Clone the current connection and replace the current prefix.
+    $connection_info = Database::getConnectionInfo('migrate');
+    if ($connection_info) {
+      Database::renameConnection('migrate', 'simpletest_original_migrate');
+    }
+    $connection_info = Database::getConnectionInfo('default');
+    foreach ($connection_info as $target => $value) {
+      $prefix = is_array($value['prefix']) ? $value['prefix']['default'] : $value['prefix'];
+      // Simpletest uses 7 character prefixes at most so this can't cause
+      // collisions.
+      $connection_info[$target]['prefix']['default'] = $prefix . '0';
+
+      // Add the original simpletest prefix so SQLite can attach its database.
+      // @see \Drupal\Core\Database\Driver\sqlite\Connection::init()
+      $connection_info[$target]['prefix'][$value['prefix']['default']] = $value['prefix']['default'];
+    }
+    Database::addConnectionInfo('migrate', 'default', $connection_info['default']);
+
+    $migration_plugins = $this->container->get('plugin.manager.migration')->getDefinitions();
+    // All the plugins provided by core depend on migrate_drupal.
+    $this->assertNotEmpty($migration_plugins);
+  }
+
+}
diff --git a/core/modules/taxonomy/tests/modules/taxonomy_term_stub_test/migrations/taxonomy_term_stub_test.yml b/core/modules/taxonomy/tests/modules/taxonomy_term_stub_test/migrations/taxonomy_term_stub_test.yml
index ddca901..ad56fff 100644
--- a/core/modules/taxonomy/tests/modules/taxonomy_term_stub_test/migrations/taxonomy_term_stub_test.yml
+++ b/core/modules/taxonomy/tests/modules/taxonomy_term_stub_test/migrations/taxonomy_term_stub_test.yml
@@ -27,3 +27,6 @@ destination:
 migration_dependencies:
   required:
     - vocabularies
+provider:
+  - migrate_drupal
+  - taxonomy
