blob: 7da98a7a61b070dbfa98cbe5cacd4e0ed3236817 [file] [log] [blame]
// 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<String> arguments = const []]) =>
runMe(arguments, createContext, "../testing.json");
Future<Context> createContext(
Chain suite, Map<String, String> environment) async {
return new Context();
}
class Context extends ChainContext {
final List<Step> steps = const <Step>[
const LintTest(),
];
// Override special handling of negative tests.
@override
Result processTestResult(
TestDescription description, Result result, bool last) {
return result;
}
List<int> rawBytes;
String cachedText;
List<int> 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<TestDescription, TestDescription, Context> {
const LintTest();
String get name => "lint test";
Future<Result<TestDescription>> 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<String> problems;
LintListener lintListener =
new LintListener((int offset, int squigglyLength, String message) {
problems ??= new List<String>();
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);
}