Skip to content

Commit d926b74

Browse files
committed
[incrParse] Add test cases to test the incremental syntax tree transfer
1 parent 8bab276 commit d926b74

File tree

11 files changed

+330
-18
lines changed

11 files changed

+330
-18
lines changed

cmake/modules/AddSwift.cmake

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2022,7 +2022,7 @@ function(_add_swift_executable_single name)
20222022
cmake_parse_arguments(SWIFTEXE_SINGLE
20232023
"EXCLUDE_FROM_ALL;DONT_STRIP_NON_MAIN_SYMBOLS;DISABLE_ASLR"
20242024
"SDK;ARCHITECTURE"
2025-
"DEPENDS;LLVM_COMPONENT_DEPENDS;LINK_LIBRARIES;LINK_FAT_LIBRARIES"
2025+
"DEPENDS;LLVM_COMPONENT_DEPENDS;LINK_LIBRARIES;LINK_FAT_LIBRARIES;COMPILE_FLAGS"
20262026
${ARGN})
20272027

20282028
set(SWIFTEXE_SINGLE_SOURCES ${SWIFTEXE_SINGLE_UNPARSED_ARGUMENTS})
@@ -2096,6 +2096,7 @@ function(_add_swift_executable_single name)
20962096
MODULE_NAME ${name}
20972097
SDK ${SWIFTEXE_SINGLE_SDK}
20982098
ARCHITECTURE ${SWIFTEXE_SINGLE_ARCHITECTURE}
2099+
COMPILE_FLAGS ${SWIFTEXE_SINGLE_COMPILE_FLAGS}
20992100
IS_MAIN)
21002101
add_swift_source_group("${SWIFTEXE_SINGLE_EXTERNAL_SOURCES}")
21012102

@@ -2260,7 +2261,7 @@ function(add_swift_executable name)
22602261
cmake_parse_arguments(SWIFTEXE
22612262
"EXCLUDE_FROM_ALL;DONT_STRIP_NON_MAIN_SYMBOLS;DISABLE_ASLR"
22622263
""
2263-
"DEPENDS;LLVM_COMPONENT_DEPENDS;LINK_LIBRARIES"
2264+
"DEPENDS;LLVM_COMPONENT_DEPENDS;LINK_LIBRARIES;COMPILE_FLAGS"
22642265
${ARGN})
22652266

22662267
translate_flag(${SWIFTEXE_EXCLUDE_FROM_ALL}
@@ -2283,6 +2284,7 @@ function(add_swift_executable name)
22832284
LINK_LIBRARIES ${SWIFTEXE_LINK_LIBRARIES}
22842285
SDK ${SWIFT_HOST_VARIANT_SDK}
22852286
ARCHITECTURE ${SWIFT_HOST_VARIANT_ARCH}
2287+
COMPILE_FLAGS ${SWIFTEXE_COMPILE_FLAGS}
22862288
${SWIFTEXE_EXCLUDE_FROM_ALL_FLAG}
22872289
${SWIFTEXE_DONT_STRIP_NON_MAIN_SYMBOLS_FLAG}
22882290
${SWIFTEXE_DISABLE_ASLR_FLAG})
@@ -2301,11 +2303,16 @@ function(add_swift_host_tool executable)
23012303
ADDSWIFTHOSTTOOL # prefix
23022304
"" # options
23032305
"" # single-value args
2304-
"SWIFT_COMPONENT" # multi-value args
2306+
"SWIFT_COMPONENT;COMPILE_FLAGS;DEPENDS" # multi-value args
23052307
${ARGN})
23062308

23072309
# Create the executable rule.
2308-
add_swift_executable(${executable} ${ADDSWIFTHOSTTOOL_UNPARSED_ARGUMENTS})
2310+
add_swift_executable(
2311+
${executable}
2312+
${ADDSWIFTHOSTTOOL_UNPARSED_ARGUMENTS}
2313+
DEPENDS ${ADDSWIFTHOSTTOOL_DEPENDS}
2314+
COMPILE_FLAGS ${ADDSWIFTHOSTTOOL_COMPILE_FLAGS}
2315+
)
23092316

23102317
# And then create the install rule if we are asked to.
23112318
if (ADDSWIFTHOSTTOOL_SWIFT_COMPONENT)

test/incrParse/simple.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,16 @@
1313
// RUN: %validate-incrparse %s --test-case LAST_CHARACTER_OF_STRUCT
1414
// RUN: %validate-incrparse %s --test-case ADD_ARRAY_CLOSE_BRACKET
1515
// RUN: %validate-incrparse %s --test-case ADD_IF_OPEN_BRACE
16+
// We need to require macOS since %incr-transfer-roundtrip uses swiftSyntax
17+
// which is not available on Linux
18+
// FIXME: Remove the requires flag when swiftSyntax builds on Linux
19+
// REQUIRES: OS=macosx
20+
// RUN: %incr-transfer-roundtrip %s --test-case REPLACE
21+
// RUN: %incr-transfer-roundtrip %s --test-case INSERT
22+
// RUN: %incr-transfer-roundtrip %s --test-case REMOVE
23+
// RUN: %incr-transfer-roundtrip %s --test-case CLASS_SURROUNDING
24+
// RUN: %incr-transfer-roundtrip %s --test-case MULTI_EDIT
25+
// RUN: %incr-transfer-roundtrip %s --test-case REPLACE_WITH_MULTI_BYTE_CHAR
1626

1727
func start() {}
1828

test/lit.cfg

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,7 @@ config.sil_passpipeline_dumper = inferSwiftBinary('sil-passpipeline-dumper')
256256
config.lldb_moduleimport_test = inferSwiftBinary('lldb-moduleimport-test')
257257
config.swift_ide_test = inferSwiftBinary('swift-ide-test')
258258
config.swift_syntax_test = inferSwiftBinary('swift-syntax-test')
259+
config.swift_swiftsyntax_test = inferSwiftBinary('swift-swiftsyntax-test')
259260
config.swift_reflection_dump = inferSwiftBinary('swift-reflection-dump')
260261
config.swift_remoteast_test = inferSwiftBinary('swift-remoteast-test')
261262
config.swift_format = inferSwiftBinary('swift-format')
@@ -345,6 +346,7 @@ ccp_opt = "-completion-cache-path %r" % completion_cache_path
345346
lit_config.note("Using code completion cache: " + completion_cache_path)
346347

347348
config.substitutions.append( ('%validate-incrparse', '%utils/incrparse/validate_parse.py --temp-dir %t --swift-syntax-test %swift-syntax-test') )
349+
config.substitutions.append( ('%incr-transfer-roundtrip', '%%utils/incrparse/incr_transfer_round_trip.py --temp-dir %%t --swift-syntax-test %%swift-syntax-test --swift-swiftsyntax-test %r' % (config.swift_swiftsyntax_test)) )
348350
config.substitutions.append( ('%swift_obj_root', config.swift_obj_root) )
349351
config.substitutions.append( ('%swift_src_root', config.swift_src_root) )
350352
config.substitutions.append( ('%{python}', sys.executable) )

tools/SwiftSyntax/CMakeLists.txt

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,3 @@ add_swift_library(swiftSwiftSyntax SHARED
2828
INSTALL_IN_COMPONENT swift-syntax
2929
TARGET_SDKS OSX
3030
IS_STDLIB)
31-
32-
add_custom_target(swiftSwiftSyntax
33-
DEPENDS swiftSwiftSyntax-macosx
34-
swiftSwiftSyntax-macosx-x86_64
35-
swiftSwiftSyntax-swiftmodule-macosx-x86_64)
Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
set(SWIFT_SYNTAX_PATH "${SWIFT_LIBRARY_OUTPUT_INTDIR}/swift/macosx/x86_64")
2-
set(EXTRA_COMPILE_FLAGS "-I" "${SWIFT_SYNTAX_PATH}")
3-
set(EXTRA_LINKER_FLAGS "-Xlinker" "-rpath" "-Xlinker" "${SWIFT_SYNTAX_PATH}")
4-
51
add_swift_host_tool(swift-swiftsyntax-test
6-
swift-syntax-test.swift
7-
DEPENDS swiftSwiftSyntax
2+
main.swift
3+
CommandLineArguments.swift
4+
COMPILE_FLAGS "-module-name" "main"
5+
DEPENDS
6+
swiftSwiftSyntax-macosx
7+
swiftSwiftSyntax-macosx-x86_64
8+
swiftSwiftSyntax-swiftmodule-macosx-x86_64
89
)
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import Foundation
2+
3+
struct CommandLineArguments {
4+
struct MissingArgumentError: LocalizedError {
5+
let argName: String
6+
7+
var errorDescription: String? {
8+
return "Missing required argument: \(argName)"
9+
}
10+
}
11+
struct UnkeyedArgumentError: LocalizedError {
12+
let argName: String
13+
14+
var errorDescription: String? {
15+
return "Unexpectedly found command line argument \(argName) without a key"
16+
}
17+
}
18+
19+
private let args: [String: String]
20+
21+
static func parse<T: Sequence>(_ args: T) throws -> CommandLineArguments
22+
where T.Element == String {
23+
var parsedArgs: [String: String] = [:]
24+
var currentKey: String? = nil
25+
for arg in args {
26+
if arg.hasPrefix("--") {
27+
// Parse a new key
28+
if let currentKey = currentKey {
29+
// The last key didn't have a value. Just add it with an empty string as
30+
// the value to the parsed args
31+
parsedArgs[currentKey] = ""
32+
}
33+
currentKey = arg
34+
} else {
35+
if let currentKey = currentKey {
36+
parsedArgs[currentKey] = arg
37+
} else {
38+
throw UnkeyedArgumentError(argName: arg)
39+
}
40+
currentKey = nil
41+
}
42+
}
43+
if let currentKey = currentKey {
44+
// The last key didn't have a value. Just add it with an empty string as
45+
// the value to the parsed args
46+
parsedArgs[currentKey] = ""
47+
}
48+
return CommandLineArguments(args: parsedArgs)
49+
}
50+
51+
subscript(key: String) -> String? {
52+
return args[key]
53+
}
54+
55+
func getRequired(_ key: String) throws -> String {
56+
if let value = args[key] {
57+
return value
58+
} else {
59+
throw MissingArgumentError(argName: key)
60+
}
61+
}
62+
63+
func has(_ key: String) -> Bool {
64+
return args[key] != nil
65+
}
66+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import SwiftSyntax
2+
import Foundation
3+
4+
/// Print the given message to stderr
5+
func printerr(_ message: String, terminator: String = "\n") {
6+
FileHandle.standardError.write((message + terminator).data(using: .utf8)!)
7+
}
8+
9+
/// Print the help message
10+
func printHelp() {
11+
print("""
12+
Utility to test SwiftSyntax syntax tree deserialization.
13+
14+
Actions (must specify one):
15+
--deserialize-incremental
16+
Deserialize a full pre-edit syntax tree (--pre-edit-tree), parse an
17+
incrementally transferred post-edit syntax tree (--incr-tree) and
18+
write the source representation of the post-edit syntax tree to an
19+
out file (--out).
20+
--help
21+
Print this help message
22+
23+
Arguments:
24+
--pre-edit-tree FILENAME
25+
The path to a JSON serialized pre-edit syntax tree
26+
--incr-tree FILENAME
27+
The path to a JSON serialized incrementally transferred post-edit
28+
syntax tree
29+
--out FILENAME
30+
The file to which the source representation of the post-edit syntax
31+
tree shall be written.
32+
""")
33+
}
34+
35+
func performRoundTrip(args: CommandLineArguments) throws {
36+
let preEditTreeURL = URL(fileURLWithPath: try args.getRequired("--pre-edit-tree"))
37+
let incrTreeURL = URL(fileURLWithPath: try args.getRequired("--incr-tree"))
38+
let outURL = URL(fileURLWithPath: try args.getRequired("--out"))
39+
40+
let preEditTreeData = try Data(contentsOf: preEditTreeURL)
41+
let incrTreeData = try Data(contentsOf: incrTreeURL)
42+
43+
let deserializer = SyntaxTreeDeserializer()
44+
_ = try deserializer.deserialize(preEditTreeData)
45+
let tree = try deserializer.deserialize(incrTreeData)
46+
let sourceRepresenation = tree.description
47+
try sourceRepresenation.write(to: outURL, atomically: false, encoding: .utf8)
48+
}
49+
50+
do {
51+
let args = try CommandLineArguments.parse(CommandLine.arguments.dropFirst())
52+
53+
if args.has("--deserialize-incremental") {
54+
try performRoundTrip(args: args)
55+
exit(0)
56+
} else if args.has("--help") {
57+
printHelp()
58+
exit(0)
59+
} else {
60+
printerr("""
61+
No action specified.
62+
See --help for information about available actions
63+
""")
64+
exit(1)
65+
}
66+
} catch {
67+
printerr(error.localizedDescription)
68+
printerr("Run swift-swiftsyntax-test --help for more help.")
69+
exit(1)
70+
}

tools/swift-swiftsyntax-test/swift-syntax-test.swift

Lines changed: 0 additions & 1 deletion
This file was deleted.
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
#!/usr/bin/env python
2+
3+
from __future__ import print_function
4+
5+
import argparse
6+
import os
7+
import subprocess
8+
import sys
9+
10+
from test_util import TestFailedError, serializeIncrParseMarkupFile
11+
12+
13+
def escapeCmdArg(arg):
14+
if '"' in arg or ' ' in arg:
15+
return '"%s"' % arg.replace('"', '\\"')
16+
else:
17+
return arg
18+
19+
20+
def run_command(cmd):
21+
print(' '.join([escapeCmdArg(arg) for arg in cmd]))
22+
return subprocess.check_output(cmd, stderr=subprocess.STDOUT)
23+
24+
25+
def main():
26+
parser = argparse.ArgumentParser(
27+
formatter_class=argparse.RawDescriptionHelpFormatter,
28+
description='Utility for testing incremental syntax parsing',
29+
epilog='''
30+
Based of a single template the utility generates a pre-edit and a post-edit
31+
file. It then verifies that incrementally parsing the post-edit file base
32+
on the pre-edit file results in the same syntax tree as reparsing the
33+
post-edit file from scratch.
34+
35+
To generate the pre-edit and the post-edit file from the template, it
36+
operates on markers of the form:
37+
38+
<<test_case<pre|||post>>>
39+
40+
These placeholders are replaced by:
41+
- 'pre' if a different test case than 'test_case' is run
42+
- 'pre' for the pre-edit version of 'test_case'
43+
- 'post' for the post-edit version of 'test_case'
44+
''')
45+
parser.add_argument(
46+
'file', type=argparse.FileType(),
47+
help='The template file to test')
48+
parser.add_argument(
49+
'--test-case', default='',
50+
help='The test case to execute. If no test case is specified all \
51+
unnamed substitutions are applied')
52+
parser.add_argument(
53+
'--temp-dir', required=True,
54+
help='A temporary directory where pre-edit and post-edit files can be \
55+
saved')
56+
parser.add_argument(
57+
'--swift-syntax-test', required=True,
58+
help='The path to swift-syntax-test')
59+
parser.add_argument(
60+
'--swift-swiftsyntax-test', required=True,
61+
help='The path to swift-swiftsyntax-test')
62+
63+
args = parser.parse_args(sys.argv[1:])
64+
65+
test_file = args.file.name
66+
test_file_name = os.path.basename(test_file)
67+
test_case = args.test_case
68+
temp_dir = args.temp_dir
69+
swift_syntax_test = args.swift_syntax_test
70+
swift_swiftsyntax_test = args.swift_swiftsyntax_test
71+
72+
if not os.path.exists(temp_dir):
73+
os.makedirs(temp_dir)
74+
75+
pre_edit_tree_file = temp_dir + '/' + test_file_name + '.' \
76+
+ test_case + '.pre.json'
77+
incremental_tree_file = temp_dir + '/' + test_file_name + '.' \
78+
+ test_case + '.incr.json'
79+
post_edit_source_file = temp_dir + '/' + test_file_name + '.' \
80+
+ test_case + '.post.swift'
81+
after_roundtrip_source_file = temp_dir + '/' + test_file_name + '.' \
82+
+ test_case + '.post_after_roundtrip.swift'
83+
84+
# Generate the syntax tree once incrementally and once from scratch
85+
try:
86+
serializeIncrParseMarkupFile(test_file=test_file,
87+
test_case=test_case,
88+
mode='pre-edit',
89+
serialization_mode='full',
90+
omit_node_ids=False,
91+
output_file=pre_edit_tree_file,
92+
temp_dir=temp_dir,
93+
swift_syntax_test=swift_syntax_test,
94+
print_visual_reuse_info=False)
95+
96+
serializeIncrParseMarkupFile(test_file=test_file,
97+
test_case=test_case,
98+
mode='incremental',
99+
serialization_mode='incremental',
100+
omit_node_ids=False,
101+
output_file=incremental_tree_file,
102+
temp_dir=temp_dir,
103+
swift_syntax_test=swift_syntax_test,
104+
print_visual_reuse_info=False)
105+
except TestFailedError as e:
106+
print('Test case "%s" of %s FAILed' % (test_case, test_file),
107+
file=sys.stderr)
108+
print(e.message, file=sys.stderr)
109+
sys.exit(1)
110+
111+
try:
112+
run_command([swift_swiftsyntax_test, '--deserialize-incremental'] +
113+
['--pre-edit-tree', pre_edit_tree_file] +
114+
['--incr-tree', incremental_tree_file] +
115+
['--out', after_roundtrip_source_file])
116+
except subprocess.CalledProcessError as e:
117+
print('Test case "%s" of %s FAILed' % (test_case, test_file),
118+
file=sys.stderr)
119+
print('Deserializing the swift file failed:\n', file=sys.stderr)
120+
print(e.output, file=sys.stderr)
121+
sys.exit(1)
122+
123+
# Check if the two syntax trees are the same
124+
try:
125+
run_command(
126+
[
127+
'diff', '-u',
128+
post_edit_source_file,
129+
after_roundtrip_source_file
130+
])
131+
except subprocess.CalledProcessError as e:
132+
print('Test case "%s" of %s FAILed' % (test_case, test_file),
133+
file=sys.stderr)
134+
print('Source file after incrementally transferring the syntax tree '
135+
'to swiftSyntax does not match post-edit source file:\n\n',
136+
file=sys.stderr)
137+
print(e.output, file=sys.stderr)
138+
sys.exit(1)
139+
140+
141+
if __name__ == '__main__':
142+
main()

0 commit comments

Comments
 (0)