Name conflict resolution with reserved Dart Keywords (#34)
Closes #21.
- Added reserved keyword conflict resolution to `UniqueNamer`.
- Added tests (`collision_tests/reserved_word_collision_test.dart`)
- Bug fix: UniqueNamers used by declarations are now reset to initial state before generating code. This ensures multiple calls to Library.generate will produce the same results
diff --git a/lib/src/code_generator/dart_keywords.dart b/lib/src/code_generator/dart_keywords.dart
new file mode 100644
index 0000000..2f3d4d6
--- /dev/null
+++ b/lib/src/code_generator/dart_keywords.dart
@@ -0,0 +1,70 @@
+// 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.
+
+/// Dart reserved keywords, used for resolving conflict with a name.
+///
+/// Source: https://dart.dev/guides/language/language-tour#keywords.
+const keywords = {
+ 'abstract',
+ 'else',
+ 'import',
+ 'super',
+ 'as',
+ 'enum',
+ 'in',
+ 'switch',
+ 'assert',
+ 'export',
+ 'interface',
+ 'sync',
+ 'async',
+ 'extends',
+ 'is',
+ 'this',
+ 'await',
+ 'extension',
+ 'library',
+ 'throw',
+ 'break',
+ 'external',
+ 'mixin',
+ 'true',
+ 'case',
+ 'factory',
+ 'new',
+ 'try',
+ 'catch',
+ 'false',
+ 'null',
+ 'typedef',
+ 'class',
+ 'final',
+ 'on',
+ 'var',
+ 'const',
+ 'finally',
+ 'operator',
+ 'void',
+ 'continue',
+ 'for',
+ 'part',
+ 'while',
+ 'covariant',
+ 'Function',
+ 'rethrow',
+ 'with',
+ 'default',
+ 'get',
+ 'return',
+ 'yield',
+ 'deferred',
+ 'hide',
+ 'set',
+ 'do',
+ 'if',
+ 'show',
+ 'dynamic',
+ 'implements',
+ 'static'
+};
diff --git a/lib/src/code_generator/library.dart b/lib/src/code_generator/library.dart
index 6c292fd..10b73a9 100644
--- a/lib/src/code_generator/library.dart
+++ b/lib/src/code_generator/library.dart
@@ -31,7 +31,6 @@
final lookUpBindings = bindings.whereType<LookUpBinding>().toList();
final noLookUpBindings = bindings.whereType<NoLookUpBinding>().toList();
- //TODO(21): Resolve dart keyword as identifiers.
/// Handle any declaration-declaration name conflict in [lookUpBindings].
final lookUpDeclConflictHandler = UniqueNamer({});
for (final b in lookUpBindings) {
diff --git a/lib/src/code_generator/utils.dart b/lib/src/code_generator/utils.dart
index 800b7f8..6bc9f2c 100644
--- a/lib/src/code_generator/utils.dart
+++ b/lib/src/code_generator/utils.dart
@@ -1,6 +1,15 @@
+import 'dart_keywords.dart';
+
class UniqueNamer {
final Set<String> _usedUpNames;
- UniqueNamer(this._usedUpNames);
+
+ /// Creates a UniqueNamer with given [usedUpNames] and Dart reserved keywords.
+ UniqueNamer(Set<String> usedUpNames)
+ : assert(keywords.intersection(usedUpNames).isEmpty),
+ _usedUpNames = {...keywords, ...usedUpNames};
+
+ /// Creates a UniqueNamer with given [usedUpNames] only.
+ UniqueNamer._raw(this._usedUpNames);
/// Returns a unique name by appending `_<int>` to it if necessary.
///
@@ -34,6 +43,8 @@
bool isUnique(String name) {
return !_usedUpNames.contains(name);
}
+
+ UniqueNamer clone() => UniqueNamer._raw({..._usedUpNames});
}
/// Converts [text] to a dart doc comment.
diff --git a/lib/src/code_generator/writer.dart b/lib/src/code_generator/writer.dart
index 55a451a..b46c4f9 100644
--- a/lib/src/code_generator/writer.dart
+++ b/lib/src/code_generator/writer.dart
@@ -27,6 +27,11 @@
String _dylibIdentifier;
String get dylibIdentifier => _dylibIdentifier;
+ /// Initial namers set after running constructor. Namers are reset to this
+ /// initial state everytime [generate] is called.
+ UniqueNamer _initialTopLevelUniqueNamer, _initialWrapperLevelUniqueNamer;
+
+ /// Used by [Binding]s for generating required code.
UniqueNamer _topLevelUniqueNamer, _wrapperLevelUniqueNamer;
UniqueNamer get topLevelUniqueNamer => _topLevelUniqueNamer;
UniqueNamer get wrapperLevelUniqueNamer => _wrapperLevelUniqueNamer;
@@ -51,20 +56,20 @@
..addAll(globalLevelNameSet)
..addAll(wrapperLevelNameSet);
- _topLevelUniqueNamer = UniqueNamer(globalLevelNameSet);
- _wrapperLevelUniqueNamer = UniqueNamer(wrapperLevelNameSet);
+ _initialTopLevelUniqueNamer = UniqueNamer(globalLevelNameSet);
+ _initialWrapperLevelUniqueNamer = UniqueNamer(wrapperLevelNameSet);
final allLevelsUniqueNamer = UniqueNamer(allNameSet);
/// Wrapper class name must be unique among all names.
_className = allLevelsUniqueNamer.makeUnique(className);
- wrapperLevelUniqueNamer.markUsed(_className);
- topLevelUniqueNamer.markUsed(_className);
+ _initialWrapperLevelUniqueNamer.markUsed(_className);
+ _initialTopLevelUniqueNamer.markUsed(_className);
/// [_ffiLibraryPrefix] should be unique in top level.
- _ffiLibraryPrefix = topLevelUniqueNamer.makeUnique('ffi');
+ _ffiLibraryPrefix = _initialTopLevelUniqueNamer.makeUnique('ffi');
/// [_dylibIdentifier] should be unique in top level.
- _dylibIdentifier = wrapperLevelUniqueNamer.makeUnique('_dylib');
+ _dylibIdentifier = _initialTopLevelUniqueNamer.makeUnique('_dylib');
/// Finding a unique prefix for Array Helper Classes and store into
/// [_arrayHelperClassPrefix].
@@ -79,12 +84,23 @@
_arrayHelperClassPrefix = '${base}${suffixInt}';
}
}
+
+ _resetUniqueNamersNamers();
+ }
+
+ /// Resets the namers to initial state. Namers are reset before generating.
+ void _resetUniqueNamersNamers() {
+ _topLevelUniqueNamer = _initialTopLevelUniqueNamer.clone();
+ _wrapperLevelUniqueNamer = _initialWrapperLevelUniqueNamer.clone();
}
/// Writes all bindings to a String.
String generate() {
final s = StringBuffer();
+ // Reset unique namers to initial state.
+ _resetUniqueNamersNamers();
+
// Write file header (if any).
if (header != null) {
s.write(header);
diff --git a/test/collision_tests/decl_decl_collision_test.dart b/test/collision_tests/decl_decl_collision_test.dart
index 3b09841..24bd079 100644
--- a/test/collision_tests/decl_decl_collision_test.dart
+++ b/test/collision_tests/decl_decl_collision_test.dart
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:ffigen/src/code_generator.dart';
+import 'package:logging/logging.dart';
import 'package:test/test.dart';
import '../test_utils.dart';
@@ -10,7 +11,7 @@
void main() {
group('decl_decl_collision_test', () {
setUpAll(() {
- logWarnings();
+ logWarnings(Level.SEVERE);
});
test('declaration conflict', () {
final l1 = Library(name: 'Bindings', bindings: [
diff --git a/test/collision_tests/reserved_keyword_collision_test.dart b/test/collision_tests/reserved_keyword_collision_test.dart
new file mode 100644
index 0000000..a41e939
--- /dev/null
+++ b/test/collision_tests/reserved_keyword_collision_test.dart
@@ -0,0 +1,47 @@
+// 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:ffigen/src/code_generator.dart';
+import 'package:logging/logging.dart';
+import 'package:test/test.dart';
+
+import '../test_utils.dart';
+
+void main() {
+ group('reserved_keyword_collision_test', () {
+ setUpAll(() {
+ logWarnings(Level.SEVERE);
+ });
+ test('reserved keyword collision', () {
+ final l1 = Library(name: 'Bindings', bindings: [
+ Struc(name: 'abstract'),
+ Struc(name: 'if'),
+ EnumClass(name: 'return'),
+ EnumClass(name: 'export'),
+ Func(
+ name: 'show',
+ returnType: Type.nativeType(SupportedNativeType.Void)),
+ Func(
+ name: 'implements',
+ returnType: Type.nativeType(SupportedNativeType.Void)),
+ ]);
+ final l2 = Library(name: 'Bindings', bindings: [
+ Struc(name: 'abstract_1'),
+ Struc(name: 'if_1'),
+ EnumClass(name: 'return_1'),
+ EnumClass(name: 'export_1'),
+ Func(
+ name: 'show_1',
+ originalName: 'show',
+ returnType: Type.nativeType(SupportedNativeType.Void)),
+ Func(
+ name: 'implements_1',
+ originalName: 'implements',
+ returnType: Type.nativeType(SupportedNativeType.Void)),
+ ]);
+
+ expect(l1.generate(), l2.generate());
+ });
+ });
+}
diff --git a/test/header_parser_tests/function_n_struct_.dart b/test/header_parser_tests/function_n_struct_test.dart
similarity index 95%
rename from test/header_parser_tests/function_n_struct_.dart
rename to test/header_parser_tests/function_n_struct_test.dart
index e99faf0..f3c7fe1 100644
--- a/test/header_parser_tests/function_n_struct_.dart
+++ b/test/header_parser_tests/function_n_struct_test.dart
@@ -5,6 +5,7 @@
import 'package:ffigen/src/code_generator.dart';
import 'package:ffigen/src/header_parser.dart' as parser;
import 'package:ffigen/src/config_provider.dart';
+import 'package:logging/logging.dart';
import 'package:test/test.dart';
import 'package:yaml/yaml.dart' as yaml;
import 'package:ffigen/src/strings.dart' as strings;
@@ -16,7 +17,7 @@
void main() {
group('function_n_struct_test', () {
setUpAll(() {
- logWarnings();
+ logWarnings(Level.SEVERE);
expected = expectedLibrary();
actual = parser.parse(
Config.fromYaml(yaml.loadYaml('''
diff --git a/test/large_integration_tests/large_test.dart b/test/large_integration_tests/large_test.dart
index 87d9f2e..a78d903 100644
--- a/test/large_integration_tests/large_test.dart
+++ b/test/large_integration_tests/large_test.dart
@@ -5,14 +5,20 @@
import 'dart:io';
import 'package:ffigen/src/header_parser.dart';
+import 'package:logging/logging.dart';
import 'package:yaml/yaml.dart';
import 'package:ffigen/src/config_provider/config.dart';
import 'package:test/test.dart';
import 'package:ffigen/src/strings.dart' as strings;
import 'package:path/path.dart' as path;
+import '../test_utils.dart';
+
void main() {
group('large_test', () {
+ setUpAll(() {
+ logWarnings(Level.SEVERE);
+ });
test('Libclang test', () {
final config = Config.fromYaml(loadYaml('''
${strings.name}: LibClang
diff --git a/test/native_test/native_test.dart b/test/native_test/native_test.dart
index eab5dfe..c829a59 100644
--- a/test/native_test/native_test.dart
+++ b/test/native_test/native_test.dart
@@ -7,11 +7,13 @@
import 'dart:math';
import 'package:test/test.dart';
+import '../test_utils.dart';
import 'native_test_bindings.dart' as bindings;
void main() {
group('native_test', () {
setUpAll(() {
+ logWarnings();
var dylibName = 'test/native_test/native_test.so';
if (Platform.isMacOS) {
dylibName = 'test/native_test/native_test.dylib';
diff --git a/test/prefix_tests/prefix_test.dart b/test/prefix_tests/prefix_test.dart
index 0031c3b..dea22fc 100644
--- a/test/prefix_tests/prefix_test.dart
+++ b/test/prefix_tests/prefix_test.dart
@@ -23,6 +23,7 @@
void main() {
group('prefix_test', () {
setUpAll(() {
+ logWarnings();
expected = expectedLibrary();
actual = parser.parse(Config.fromYaml(yaml.loadYaml('''
${strings.name}: 'NativeLibrary'
diff --git a/test/test_utils.dart b/test/test_utils.dart
index 4b9d9eb..8d49c18 100644
--- a/test/test_utils.dart
+++ b/test/test_utils.dart
@@ -41,8 +41,8 @@
}
}
-void logWarnings() {
- Logger.root.level = Level.WARNING;
+void logWarnings([Level level = Level.WARNING]) {
+ Logger.root.level = level;
Logger.root.onRecord.listen((record) {
print('${record.level.name.padRight(8)}: ${record.message}');
});