// Copyright (c) 2019, 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 'dart:async' show Future; import 'dart:io' show File; import 'dart:typed_data' show Uint8List; import 'package:front_end/src/fasta/parser.dart' show Parser; import 'package:front_end/src/fasta/parser/listener.dart' show Listener; import 'package:front_end/src/fasta/command_line_reporting.dart' as command_line_reporting; import 'package:front_end/src/fasta/scanner/utf8_bytes_scanner.dart' show Utf8BytesScanner; import 'package:front_end/src/scanner/token.dart' show Token; import 'package:front_end/src/scanner/token.dart'; import 'package:kernel/kernel.dart'; import 'package:testing/testing.dart' show ChainContext, Result, Step, TestDescription, Chain, runMe; main([List arguments = const []]) => runMe(arguments, createContext, "../testing.json"); Future createContext( Chain suite, Map environment) async { return new Context(); } class Context extends ChainContext { final List steps = const [ const LintTest(), ]; // Override special handling of negative tests. @override Result processTestResult( TestDescription description, Result result, bool last) { return result; } List rawBytes; String cachedText; List lineStarts; Uri uri; void clear() { rawBytes = null; cachedText = null; lineStarts = null; uri = null; } String getErrorMessage(int offset, int squigglyLength, String message) { Source source = new Source(lineStarts, rawBytes, uri, uri); Location location = source.getLocation(uri, offset); return command_line_reporting.formatErrorMessage( source.getTextLine(location.line), location, squigglyLength, uri.toString(), message); } } class LintTest extends Step { const LintTest(); String get name => "lint test"; Future> run( TestDescription description, Context context) async { context.clear(); context.uri = description.uri; File f = new File.fromUri(context.uri); context.rawBytes = f.readAsBytesSync(); Uint8List bytes = new Uint8List(context.rawBytes.length + 1); bytes.setRange(0, context.rawBytes.length, context.rawBytes); Utf8BytesScanner scanner = new Utf8BytesScanner(bytes, includeComments: true); Token firstToken = scanner.tokenize(); context.lineStarts = scanner.lineStarts; if (firstToken == null) return null; List problems; LintListener lintListener = new LintListener((int offset, int squigglyLength, String message) { problems ??= new List(); problems.add(context.getErrorMessage(offset, squigglyLength, message)); }); Parser parser = new Parser(lintListener); parser.parseUnit(firstToken); if (problems == null) { return pass(description); } return fail(description, problems.join("\n\n")); } } class LintListener extends Listener { final Function(int offset, int squigglyLength, String message) onProblem; LintListener(this.onProblem); LatestType _latestType; @override void beginVariablesDeclaration( Token token, Token lateToken, Token varFinalOrConst) { if (!_latestType.type) { onProblem( varFinalOrConst.offset, varFinalOrConst.length, "No explicit type."); } } @override void handleType(Token beginToken, Token questionMark) { _latestType = new LatestType(beginToken, true); } @override void handleNoType(Token lastConsumed) { _latestType = new LatestType(lastConsumed, false); } } class LatestType { final Token token; bool type; LatestType(this.token, this.type); }