Index: src/wp-includes/query.php
===================================================================
--- src/wp-includes/query.php	(revision 36516)
+++ src/wp-includes/query.php	(working copy)
@@ -3788,6 +3788,19 @@
 			$this->posts = array();
 		}
 
+		/**
+		 * Fires after the query has been set up and executed.
+		 *
+		 * Note: If using conditional tags, use the method versions within the passed instance
+		 * (e.g. $this->is_main_query() instead of is_main_query()). This is because the functions
+		 * like is_main_query() test against the global $wp_query instance, not the passed one.
+		 *
+		 * @since 4.5.0
+		 *
+		 * @param WP_Query &$this The WP_Query instance (passed by reference).
+		 */
+		do_action_ref_array( 'after_get_posts', array( &$this ) );
+
 		return $this->posts;
 	}
 
Index: tests/phpunit/tests/query/setupPostdata.php
===================================================================
--- tests/phpunit/tests/query/setupPostdata.php	(revision 36516)
+++ tests/phpunit/tests/query/setupPostdata.php	(working copy)
@@ -380,4 +380,20 @@
 		}
 	}
 
+	/**
+	 * @ticket 35816
+	 */
+	public function test_after_get_posts_is_called() {
+		$query  = new WP_Query;
+
+		// Add a listener to the new 'after_get_posts' action.
+		$action = new MockAction();
+		add_action( 'after_get_posts', array( &$action, 'action' ) );
+
+		// Execute the get_posts() method.
+		$query->get_posts();
+
+		$this->assertEquals( 1, $action->get_call_count() );
+	}
+
 }
