anchor.diff:  
     /// Strings interpolations can contain other string nodes. Check like this.
     bool containsString(StringInterpolation string) {
  -    final checkHasString = new _HasStringVisitor();
  +    final checkHasString = new _IsOrContainsStringVisitor();
       return string.elements.any((child) => child.accept(checkHasString));
     }
   }
  
positive.code: /// Do a top-down analysis to search for string nodes. Note, do not pass in
  /// string nodes directly to this visitor, or you will always get true. Pass in
  /// its children.
  class _HasStringVisitor extends UnifyingAstVisitor<bool> {
    /// Scan as little of the tree as possible, by bailing out on first match. For
    /// all leaf nodes, they will either have a method defined here and return
    /// true, or they will return false because leaves have no children.
    @override
    bool visitNode(AstNode node) {
      return _ImmediateChildrenVisitor
          .getChildren(node)
          .any((child) => child.accept(this));
    }
  
    @override
    bool visitSimpleStringLiteral(SimpleStringLiteral string) {
  
negative.code:   }
  }
  
  /// The only way to get immediate children in a unified, typesafe way, is to
  /// call visitChildren on that node, and pass in a visitor. This collects at the
  /// top level and stops.
  class _ImmediateChildrenVisitor extends UnifyingAstVisitor {
    static List<AstNode> childrenOf(AstNode node) {
      final visitor = new _ImmediateChildrenVisitor();
      node.visitChildren(visitor);
      return visitor._children;
    }
  
    final _children = <AstNode>[];
  
    @override
    visitNode(AstNode node) {
  