Index: wp-includes/shortcodes.php
===================================================================
--- wp-includes/shortcodes.php	(revision 45382)
+++ wp-includes/shortcodes.php	(working copy)
@@ -42,6 +42,19 @@
 $shortcode_tags = array();
 
 /**
+ * Container the keeps track of currently running shortcodes.
+ *
+ * @since 5.2.x
+ * @var array
+ * @global array $wp_current_shortcode
+ */
+global $wp_current_shortcode;
+
+if ( ! isset( $wp_current_shortcode ) ) {
+	$wp_current_shortcode = array();
+}
+
+/**
  * Adds a new shortcode.
  *
  * Care should be taken through prefixing or other means to ensure that the
@@ -160,6 +173,45 @@
 }
 
 /**
+ * Retrieve the name of the current shortcode being processed.
+ *
+ * @since 5.2.x
+ *
+ * @global array $wp_current_shortcode Stores the list of current shortcodes with the current one last.
+ *
+ * @return string Tag name of the current shortcode.
+ */
+function current_shortcode() {
+	global $wp_current_shortcode;
+	return end( $wp_current_shortcode );
+}
+
+/**
+ * Check if a shortcode is currently being processed.
+ *
+ * This function allows detection for any shortcode currently being
+ * executed (despite not being the most recent shortcode to fire, in the case of
+ * shortcodes called from shortcode callbacks) to be verified.
+ *
+ * @since 5.2.x
+ *
+ * @global array $wp_current_shortcode Current shortcode(s).
+ *
+ * @param null|string $tag Optional. Shortcode tag to check. Defaults to null, which
+ *                         checks if any shortcode is currently being run.
+ * @return bool Whether the shortcode is currently in the stack.
+ */
+function doing_shortcode( $tag = null ) {
+	global $wp_current_shortcode;
+
+	if ( null === $tag ) {
+		return ! empty( $wp_current_shortcode );
+	}
+
+	return in_array( $tag, $wp_current_shortcode );
+}
+
+/**
  * Search content for shortcodes and filter shortcodes through their hooks.
  *
  * If there are no shortcode tags defined, then the content will be returned
@@ -285,7 +337,7 @@
  * @return string|false False on failure.
  */
 function do_shortcode_tag( $m ) {
-	global $shortcode_tags;
+	global $shortcode_tags, $wp_current_shortcode;
 
 	// allow [[foo]] syntax for escaping a tag
 	if ( $m[1] == '[' && $m[6] == ']' ) {
@@ -302,6 +354,8 @@
 		return $m[0];
 	}
 
+	$wp_current_shortcode[] = $tag;
+
 	/**
 	 * Filters whether to call a shortcode callback.
 	 *
@@ -317,6 +371,7 @@
 	 */
 	$return = apply_filters( 'pre_do_shortcode_tag', false, $tag, $attr, $m );
 	if ( false !== $return ) {
+		array_pop( $wp_current_shortcode );
 		return $return;
 	}
 
@@ -334,7 +389,11 @@
 	 * @param array|string $attr   Shortcode attributes array or empty string.
 	 * @param array        $m      Regular expression match array.
 	 */
-	return apply_filters( 'do_shortcode_tag', $output, $tag, $attr, $m );
+	$return = apply_filters( 'do_shortcode_tag', $output, $tag, $attr, $m );
+
+	array_pop( $wp_current_shortcode );
+
+	return $return;
 }
 
 /**
