| Index: pkg/analyzer_plugin/tool/spec/codegen_matchers.dart
|
| diff --git a/pkg/analyzer_plugin/tool/spec/codegen_matchers.dart b/pkg/analyzer_plugin/tool/spec/codegen_matchers.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..1d18ef78dab5cdc89c56e662cdd5e4388daf568d
|
| --- /dev/null
|
| +++ b/pkg/analyzer_plugin/tool/spec/codegen_matchers.dart
|
| @@ -0,0 +1,187 @@
|
| +// Copyright (c) 2017, 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.
|
| +
|
| +/**
|
| + * Code generation for the file "matchers.dart".
|
| + */
|
| +import 'dart:convert';
|
| +
|
| +import 'package:analyzer/src/codegen/tools.dart';
|
| +
|
| +import 'api.dart';
|
| +import 'from_html.dart';
|
| +import 'implied_types.dart';
|
| +import 'to_html.dart';
|
| +
|
| +final GeneratedFile target = new GeneratedFile(
|
| + 'test/integration/support/protocol_matchers.dart', (String pkgPath) {
|
| + CodegenMatchersVisitor visitor = new CodegenMatchersVisitor(readApi(pkgPath));
|
| + return visitor.collectCode(visitor.visitApi);
|
| +});
|
| +
|
| +class CodegenMatchersVisitor extends HierarchicalApiVisitor with CodeGenerator {
|
| + /**
|
| + * Visitor used to produce doc comments.
|
| + */
|
| + final ToHtmlVisitor toHtmlVisitor;
|
| +
|
| + /**
|
| + * Short human-readable string describing the context of the matcher being
|
| + * created.
|
| + */
|
| + String context;
|
| +
|
| + CodegenMatchersVisitor(Api api)
|
| + : toHtmlVisitor = new ToHtmlVisitor(api),
|
| + super(api) {
|
| + codeGeneratorSettings.commentLineLength = 79;
|
| + codeGeneratorSettings.languageName = 'dart';
|
| + }
|
| +
|
| + /**
|
| + * Create a matcher for the part of the API called [name], optionally
|
| + * clarified by [nameSuffix]. The matcher should verify that its input
|
| + * matches the given [type].
|
| + */
|
| + void makeMatcher(ImpliedType impliedType) {
|
| + context = impliedType.humanReadableName;
|
| + docComment(toHtmlVisitor.collectHtml(() {
|
| + toHtmlVisitor.p(() {
|
| + toHtmlVisitor.write(context);
|
| + });
|
| + if (impliedType.type != null) {
|
| + toHtmlVisitor.showType(null, impliedType.type);
|
| + }
|
| + }));
|
| + write('final Matcher ${camelJoin(['is', impliedType.camelName])} = ');
|
| + if (impliedType.type == null) {
|
| + write('isNull');
|
| + } else {
|
| + visitTypeDecl(impliedType.type);
|
| + }
|
| + writeln(';');
|
| + writeln();
|
| + }
|
| +
|
| + /**
|
| + * Generate a map describing the given set of fields, for use as the
|
| + * 'requiredFields' or 'optionalFields' argument to the [MatchesJsonObject]
|
| + * constructor.
|
| + */
|
| + void outputObjectFields(Iterable<TypeObjectField> fields) {
|
| + if (fields.isEmpty) {
|
| + write('null');
|
| + return;
|
| + }
|
| + writeln('{');
|
| + indent(() {
|
| + bool commaNeeded = false;
|
| + for (TypeObjectField field in fields) {
|
| + if (commaNeeded) {
|
| + writeln(',');
|
| + }
|
| + write('${JSON.encode(field.name)}: ');
|
| + if (field.value != null) {
|
| + write('equals(${JSON.encode(field.value)})');
|
| + } else {
|
| + visitTypeDecl(field.type);
|
| + }
|
| + commaNeeded = true;
|
| + }
|
| + writeln();
|
| + });
|
| + write('}');
|
| + }
|
| +
|
| + @override
|
| + visitApi() {
|
| + outputHeader(year: '2017');
|
| + writeln();
|
| + writeln('/**');
|
| + writeln(' * Matchers for data types defined in the analysis server API');
|
| + writeln(' */');
|
| + writeln("import 'package:test/test.dart';");
|
| + writeln();
|
| + writeln("import 'integration_tests.dart';");
|
| + writeln();
|
| + for (ImpliedType impliedType in computeImpliedTypes(api).values) {
|
| + makeMatcher(impliedType);
|
| + }
|
| + }
|
| +
|
| + @override
|
| + visitTypeEnum(TypeEnum typeEnum) {
|
| + writeln('new MatchesEnum(${JSON.encode(context)}, [');
|
| + indent(() {
|
| + bool commaNeeded = false;
|
| + for (TypeEnumValue value in typeEnum.values) {
|
| + if (commaNeeded) {
|
| + writeln(',');
|
| + }
|
| + write('${JSON.encode(value.value)}');
|
| + commaNeeded = true;
|
| + }
|
| + writeln();
|
| + });
|
| + write('])');
|
| + }
|
| +
|
| + @override
|
| + visitTypeList(TypeList typeList) {
|
| + write('isListOf(');
|
| + visitTypeDecl(typeList.itemType);
|
| + write(')');
|
| + }
|
| +
|
| + @override
|
| + visitTypeMap(TypeMap typeMap) {
|
| + write('isMapOf(');
|
| + visitTypeDecl(typeMap.keyType);
|
| + write(', ');
|
| + visitTypeDecl(typeMap.valueType);
|
| + write(')');
|
| + }
|
| +
|
| + @override
|
| + void visitTypeObject(TypeObject typeObject) {
|
| + writeln('new LazyMatcher(() => new MatchesJsonObject(');
|
| + indent(() {
|
| + write('${JSON.encode(context)}, ');
|
| + Iterable<TypeObjectField> requiredFields =
|
| + typeObject.fields.where((TypeObjectField field) => !field.optional);
|
| + outputObjectFields(requiredFields);
|
| + List<TypeObjectField> optionalFields = typeObject.fields
|
| + .where((TypeObjectField field) => field.optional)
|
| + .toList();
|
| + if (optionalFields.isNotEmpty) {
|
| + write(', optionalFields: ');
|
| + outputObjectFields(optionalFields);
|
| + }
|
| + });
|
| + write('))');
|
| + }
|
| +
|
| + @override
|
| + void visitTypeReference(TypeReference typeReference) {
|
| + String typeName = typeReference.typeName;
|
| + if (typeName == 'long') {
|
| + typeName = 'int';
|
| + }
|
| + write(camelJoin(['is', typeName]));
|
| + }
|
| +
|
| + @override
|
| + void visitTypeUnion(TypeUnion typeUnion) {
|
| + bool commaNeeded = false;
|
| + write('isOneOf([');
|
| + for (TypeDecl choice in typeUnion.choices) {
|
| + if (commaNeeded) {
|
| + write(', ');
|
| + }
|
| + visitTypeDecl(choice);
|
| + commaNeeded = true;
|
| + }
|
| + write('])');
|
| + }
|
| +}
|
|
|