Skip to content

Commit 19c1f20

Browse files
authored
fix: Fixed the escaper to simplify the plugin syntax (#1817)
* Fixed the escaper to simplify the syntax * Review comments
1 parent 460d456 commit 19c1f20

File tree

4 files changed

+28
-25
lines changed

4 files changed

+28
-25
lines changed

Source/Dafny/DafnyOptions.cs

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -238,15 +238,6 @@ protected override bool ParseOption(string name, Bpl.CommandLineOptionEngine.Com
238238
case "plugin": {
239239
if (ps.ConfirmArgumentCount(1)) {
240240
var pluginAndArgument = args[ps.i];
241-
if (pluginAndArgument.Length > 0 &&
242-
pluginAndArgument[0] == '"' &&
243-
pluginAndArgument[^1] == '"'
244-
) {
245-
var unescapeRegex = new Regex(@"\\""|\\\\");
246-
pluginAndArgument = unescapeRegex.Replace(pluginAndArgument.Substring(1, pluginAndArgument.Length - 2),
247-
match => match.Groups[0].Value == @"\""" ? "\"" : @"\"
248-
);
249-
}
250241
if (pluginAndArgument.Length > 0) {
251242
var pluginArray = pluginAndArgument.Split(',');
252243
var pluginPath = pluginArray[0];
@@ -255,12 +246,7 @@ protected override bool ParseOption(string name, Bpl.CommandLineOptionEngine.Com
255246
// There are no commas in paths, but there can be in arguments
256247
var argumentsString = string.Join(',', pluginArray.Skip(1));
257248
// Parse arguments, accepting and remove double quotes that isolate long arguments
258-
var splitter = new Regex(@"""((?:[^""]|\\"")*)""|([^ ]+)");
259-
arguments = splitter.Matches(argumentsString).Select(
260-
matchResult => matchResult.Groups[1].Success ?
261-
matchResult.Groups[1].Value.Replace(@"\""", @"""") :
262-
matchResult.Groups[2].Value
263-
).ToArray();
249+
arguments = ParsePluginArguments(argumentsString);
264250
}
265251
Plugins.Add(Plugin.Load(pluginPath, arguments));
266252
}
@@ -546,6 +532,18 @@ protected override bool ParseOption(string name, Bpl.CommandLineOptionEngine.Com
546532
return TestGenOptions.ParseOption(name, ps) || base.ParseOption(name, ps);
547533
}
548534

535+
private static string[] ParsePluginArguments(string argumentsString) {
536+
var splitter = new Regex(@"""(?<escapedArgument>(?:[^""\\]|\\\\|\\"")*)""|(?<rawArgument>[^ ]+)");
537+
var escapedChars = new Regex(@"(?<escapedDoubleQuote>\\"")|\\\\");
538+
return splitter.Matches(argumentsString).Select(
539+
matchResult =>
540+
matchResult.Groups["escapedArgument"].Success
541+
? escapedChars.Replace(matchResult.Groups["escapedArgument"].Value,
542+
matchResult2 => matchResult2.Groups["escapedDoubleQuote"].Success ? "\"" : "\\")
543+
: matchResult.Groups["rawArgument"].Value
544+
).ToArray();
545+
}
546+
549547
protected void InvalidArgumentError(string name, CommandLineParseState ps) {
550548
ps.Error("Invalid argument \"{0}\" to option {1}", ps.args[ps.i], name);
551549
}

Source/DafnyLanguageServer.Test/Various/PluginsAdvancedTest.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public async Task SetUp() {
1919
"PluginsAdvancedTest";
2020

2121
protected override string[] CommandLineArgument =>
22-
new[] { $@"--dafny:plugins:0=""{LibraryPath},force you""" };
22+
new[] { $"--dafny:plugins:0={LibraryPath},force you" };
2323

2424
[TestMethod]
2525
public async Task EnsureErrorMessageCanBeComplexAndTakeIntoAccountConfiguration() {
@@ -35,7 +35,7 @@ public async Task EnsureErrorMessageCanBeComplexAndTakeIntoAccountConfiguration(
3535
var resolutionReport = await DiagnosticReceiver.AwaitNextNotificationAsync(CancellationToken.None);
3636
Assert.AreEqual(documentItem.Uri, resolutionReport.Uri);
3737
var diagnostics = resolutionReport.Diagnostics.ToArray();
38-
Assert.AreEqual(1, DafnyOptions.O.Plugins.Count, "Too many plugins loaded");
38+
Assert.AreEqual(1, DafnyOptions.O.Plugins.Count, "Not exactly 1 plugin loaded");
3939
Assert.AreEqual(1, diagnostics.Length, LibraryPath + " did not raise an error.");
4040
Assert.AreEqual("Please declare a method {:test} named myMethod_test that will call myMethod, you", diagnostics[0].Message);
4141
Assert.AreEqual(new Range((1, 17), (1, 25)), diagnostics[0].Range);

Source/DafnyLanguageServer.Test/Various/PluginsTest.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public async Task SetUp() {
1818
"PluginsTest";
1919

2020
protected override string[] CommandLineArgument =>
21-
new[] { $@"--dafny:plugins:0=""{LibraryPath},\""because\\ whatever\""""" };
21+
new[] { $@"--dafny:plugins:0={LibraryPath},""because\\ \""whatever""" };
2222

2323
[TestMethod]
2424
public async Task EnsureItIsPossibleToLoadAPluginWithArguments() {
@@ -28,9 +28,9 @@ public async Task EnsureItIsPossibleToLoadAPluginWithArguments() {
2828
var resolutionReport = await DiagnosticReceiver.AwaitNextNotificationAsync(CancellationToken.None);
2929
Assert.AreEqual(documentItem.Uri, resolutionReport.Uri);
3030
var diagnostics = resolutionReport.Diagnostics.ToArray();
31-
Assert.AreEqual(DafnyOptions.O.Plugins.Count, 1, "Too many plugins loaded");
31+
Assert.AreEqual(DafnyOptions.O.Plugins.Count, 1, "Not exactly 1 plugin loaded");
3232
Assert.AreEqual(1, diagnostics.Length, LibraryPath + " did not raise an error.");
33-
Assert.AreEqual("Impossible to continue because\\ whatever", diagnostics[0].Message);
33+
Assert.AreEqual("Impossible to continue because\\ \"whatever", diagnostics[0].Message);
3434
Assert.AreEqual(new Range((0, 9), (0, 13)), diagnostics[0].Range);
3535
}
3636

Source/DafnyLanguageServer/README.md

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,12 +79,17 @@ Options provided through the command line have higher priority than the options
7979
### Plugins
8080

8181
```sh
82-
# Provides a path to assemblies and optional space-separated command-line arguments after a commo.
83-
# Easier to use via VSCode's Settings interface (no need to escape inner double quotes)
82+
# Provides a path to assemblies and optional space-separated command-line arguments after a comma.
8483
# Repeat with --dafny:plugins:0=... --dafny:plugins:1=... for multiple plugins.
85-
--dafny:plugins:0=example.dll
86-
--dafny:plugins:0=example2.dll,oneArgument
87-
"--dafny:plugins:0=\"example2.dll,\\\"firstArgument with space\\\" secondArgument\""
84+
--dafny:plugins:0=path\to\example.dll
85+
--dafny:plugins:0=path/to/example2.dll,oneArgument
86+
--dafny:plugins:0=example3.dll,"firstArgument with \" space and quote" secondArgument
87+
88+
# On the command-line, you'd use the following escapes for the first and third examples:
89+
"--dafny:plugins:0=path\\to\\example.dll"
90+
"--dafny:plugins:0=example3.dll,\"firstArgument with \\\" space and quote\" secondArgument"
91+
92+
# For just the dafny executable, replace `--dafny:plugins:X` by `--plugin:` that you can repeat.
8893
```
8994

9095
#### About plugins

0 commit comments

Comments
 (0)