anchor.diff:      assert(_debugLifecycleState == _ElementLifecycle.active);
       assert(() {
         if (owner._debugBuilding) {
  -        if (owner._debugCurrentBuildTarget == null) {
  -          // If _debugCurrentBuildTarget is null, we're not actually building a
  -          // widget but instead building the root of the tree via runApp.
  -          // TODO(abarth): Remove these cases and ensure that we always have
  -          // a current build target when we're building.
  -          return true;
  -        }
  +        assert(owner._debugCurrentBuildTarget != null);
  +        assert(owner._debugStateLocked);
           if (_debugIsInScope(owner._debugCurrentBuildTarget))
             return true;
  -      }
  -      if (owner._debugStateLocked && (!_debugAllowIgnoredCallsToMarkNeedsBuild || !dirty)) {
  +        if (!_debugAllowIgnoredCallsToMarkNeedsBuild) {
  +          throw new FlutterError(
  +            'setState() or markNeedsBuild() called during build.\n'
  +            'This ${widget.runtimeType} widget cannot be marked as needing to build because the framework '
  +            'is already in the process of building widgets. A widget can be marked as '
  +            'needing to be built during the build phase only if one of its ancestors '
  +            'is currently building. This exception is allowed because the framework '
  +            'builds parent widgets before children, which means a dirty descendant '
  +            'will always be built. Otherwise, the framework might not visit this '
  +            'widget during this build phase.\n'
  +            'The widget on which setState() or markNeedsBuild() was called was:\n'
  +            '  $this\n'
  +            '${owner._debugCurrentBuildTarget == null ? "" : "The widget which was currently being built when the offending call was made was:\n  ${owner._debugCurrentBuildTarget}"}'
  +          );
  +        }
  +        assert(dirty); // can only get here if we're not in scope, but ignored calls are allowed, and our call would somehow be ignored (since we're already dirty)
  +      } else if (owner._debugStateLocked) {
  +        assert(!_debugAllowIgnoredCallsToMarkNeedsBuild);
           throw new FlutterError(
  -          'setState() or markNeedsBuild() called during build.\n'
  -          'This widget cannot be marked as needing to build because the framework '
  -          'is already in the process of building widgets. A widget can be marked as '
  -          'needing to be built during the build phase only if one if its ancestors '
  -          'is currently building. This exception is allowed because the framework '
  -          'builds parent widgets before children, which means a dirty descendant '
  -          'will always be built. Otherwise, the framework might not visit this '
  -          'widget during this build phase.'
  +          'setState() or markNeedsBuild() called when widget tree was locked.\n'
  +          'This ${widget.runtimeType} widget cannot be marked as needing to build '
  +          'because the framework is locked.\n'
  +          'The widget on which setState() or markNeedsBuild() was called was:\n'
  +          '  $this\n'
           );
         }
         return true;
  
positive.code:   /// Only one [buildScope] can be active at a time.
    ///
    /// A [buildScope] implies a [lockState] scope as well.
    void buildScope(Element context, [VoidCallback callback]) {
      if (callback == null && _dirtyElements.isEmpty)
        return;
      assert(context != null);
  
negative.code: }
  
  /// An [Element] that can be marked dirty and rebuilt.
  ///
  /// In practice, all subclasses of [Element] in the Flutter framework are
  /// subclasses of [BuildableElement]. The distinction exists primarily to
  /// segregate unrelated code.
  abstract class BuildableElement extends Element {
    /// Creates an element that uses the given widget as its configuration.
    BuildableElement(Widget widget) : super(widget);
  