// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:analyzer/dart/ast/ast.dart';
import 'package:scrape/scrape.dart';

final quoteRegExp = RegExp('\\\\?[\'"]');

void main(List<String> arguments) {
  Scrape()
    ..addHistogram('Directive')
    ..addHistogram('Expression')
    ..addHistogram('Escapes')
    ..addVisitor(() => StringVisitor())
    ..runCommandLine(arguments);
}

class StringVisitor extends ScrapeVisitor {
  @override
  void visitSimpleStringLiteral(SimpleStringLiteral node) {
    _record('Expression', node);
    _recordEscapes(node);
    super.visitSimpleStringLiteral(node);
  }

  @override
  void visitStringInterpolation(StringInterpolation node) {
    // Entire expression containing interpolation elements.
    _record('Expression', node);

    // TODO: Analyze escaped quotes inside strings.

    // TODO: Analyze string literals nested inside interpolation.
    super.visitStringInterpolation(node);
  }

  @override
  void visitExportDirective(ExportDirective node) {
    _record('Directive', node.uri);
    // Don't recurse so that we don't treat the URI as a string expression.
  }

  @override
  void visitImportDirective(ImportDirective node) {
    _record('Directive', node.uri);
    // Don't recurse so that we don't treat the URI as a string expression.
  }

  @override
  void visitPartDirective(PartDirective node) {
    _record('Directive', node.uri);
    // Don't recurse so that we don't treat the URI as a string expression.
  }

  @override
  void visitPartOfDirective(PartOfDirective node) {
    if (node.uri != null) _record('Directive', node.uri);
    // Don't recurse so that we don't treat the URI as a string expression.
  }

  void _record(String histogram, StringLiteral string) {
    record(histogram, _quoteType(string.beginToken.lexeme));
  }

  void _recordEscapes(StringLiteral string) {
    var quote = _quoteType(string.beginToken.lexeme);

    // Ignore the rarer quote styles.
    if (quote != "'" && quote != '"') return;

    var contents = string.toSource();
    contents = contents.substring(1, contents.length - 1);

    var quotes = quoteRegExp
        .allMatches(contents)
        .map((match) => match[0])
        .toSet()
        .toList();
    quotes.sort();

    if (quotes.isNotEmpty) {
      record('Escapes', '$quote containing ${quotes.join(" ")}');
    }
  }

  String _quoteType(String lexeme) {
    const types = ['"""', "'''", 'r"""', "r'''", '"', "'", 'r"', "r'"];

    for (var type in types) {
      if (lexeme.startsWith(type)) return type;
    }

    log('Unknown string quote in $lexeme');
    return '';
  }
}

Add a script to scrape string literal quotation data.
Add a script to scrape string literal quotation data.

Change-Id: Ie338fddece20db0109ecbc2c508d8b3cd0d45754
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/175781
Auto-Submit: Bob Nystrom <afede3a9ebedb865bee78398feff6fbe97068f17@google.com>
Commit-Queue: Phil Quitslund <3faecbdd7b7be83f493f037bf1d597e6bb9d0848@google.com>
Reviewed-by: Phil Quitslund <3faecbdd7b7be83f493f037bf1d597e6bb9d0848@google.com>
