Skip to content

Commit 0f00c2b

Browse files
committed
Corrections to ',' and '+' escaping for type names in quotations
fixes #270 closes #275 commit e97a592 Author: latkin <[email protected]> Date: Thu Feb 26 12:46:27 2015 -0800 Added comments commit 24a77be Author: latkin <[email protected]> Date: Thu Feb 26 12:20:23 2015 -0800 Add new tests to CI smoke suite commit a0dd05e Author: latkin <[email protected]> Date: Thu Feb 26 12:01:27 2015 -0800 Test cases for commas in type name commit bd69602 Author: latkin <[email protected]> Date: Thu Feb 26 12:00:53 2015 -0800 Mini test framework for validating multitargeting matrix commit e9772de Author: latkin <[email protected]> Date: Thu Feb 26 07:56:51 2015 -0800 Corrections to ',' and '+' escaping for type names in quotations
1 parent a1a27a4 commit 0f00c2b

File tree

10 files changed

+166
-21
lines changed

10 files changed

+166
-21
lines changed

src/absil/ilreflect.fs

+1-19
Original file line numberDiff line numberDiff line change
@@ -324,25 +324,7 @@ type cenv =
324324
// [ns] ,name -> ns+name
325325
// [ns;typeA;typeB],name -> ns+typeA+typeB+name
326326
let convTypeRefAux (cenv:cenv) (tref:ILTypeRef) =
327-
328-
// If an inner nested type's name contains a space, the proper encoding is "\+" on both sides - otherwise,
329-
// we use "+"
330-
let rec collectPrefixParts (l : string list) (acc : string list) =
331-
match l with
332-
| h1 :: (h2 :: _ as tl) ->
333-
collectPrefixParts tl
334-
(List.append
335-
acc
336-
[ yield h1
337-
if h1.Contains(" ") || h2.Contains(" ") then
338-
yield "\\+"
339-
else
340-
yield "+"])
341-
| h :: [] -> List.append acc [h]
342-
| _ -> acc
343-
344-
let prefix = collectPrefixParts tref.Enclosing [] |> List.fold (fun (s1 : string) (s2 : string) -> s1 + s2) ""
345-
let qualifiedName = prefix + (if prefix <> "" then (if tref.Name.Contains(" ") then "\\+" else "+") else "") + tref.Name // e.g. Name.Space.Class+NestedClass
327+
let qualifiedName = (String.concat "+" (tref.Enclosing @ [ tref.Name ])).Replace(",", @"\,")
346328
match tref.Scope with
347329
| ILScopeRef.Assembly asmref ->
348330
let assembly =

src/fsharp/FSharp.Core/quotations.fs

+4-1
Original file line numberDiff line numberDiff line change
@@ -1298,8 +1298,11 @@ module Patterns =
12981298
if System.Int32.TryParse(a, &idx) && b = "" then
12991299
st.referencedTypeDefs.[idx]
13001300
else
1301+
// escape commas found in type name, which are not already escaped
1302+
// '\' is not valid in a type name except as an escape character, so logic can be pretty simple
1303+
let escapedTcName = System.Text.RegularExpressions.Regex.Replace(a, @"(?<!\\),", @"\,")
13011304
let assref = decodeAssemblyRef st b
1302-
mkNamedTycon (a,assref)
1305+
mkNamedTycon (escapedTcName, assref)
13031306

13041307
let u_tyconstSpec st =
13051308
let tag = u_byte_as_int st

tests/RunTests.cmd

+5
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,9 @@ IF NOT DEFINED GACUTILEXE64 IF EXIST "%WINSDKNETFXTOOLS%x64\gacutil.exe" set GAC
126126
set FSC=%FSCBINPATH%\fsc.exe
127127
set PATH=%FSCBINPATH%;%PATH%
128128

129+
set FSCVPREVBINPATH=%X86_PROGRAMFILES%\Microsoft SDKs\F#\3.1\Framework\v4.0
130+
set FSCVPREV=%FSCVPREVBINPATH%\fsc.exe
131+
129132
REM == VS-installed paths to FSharp.Core.dll
130133
set FSCOREDLLPATH=%X86_PROGRAMFILES%\Reference Assemblies\Microsoft\FSharp\.NETFramework\v4.0\4.4.0.0
131134
set FSCOREDLL20PATH=%X86_PROGRAMFILES%\Reference Assemblies\Microsoft\FSharp\.NETFramework\v2.0\2.3.0.0
@@ -134,6 +137,7 @@ set FSCOREDLLNETCOREPATH=%X86_PROGRAMFILES%\Reference Assemblies\Microsoft\FShar
134137
set FSCOREDLLNETCORE78PATH=%X86_PROGRAMFILES%\Reference Assemblies\Microsoft\FSharp\.NETCore\3.78.4.0
135138
set FSCOREDLLNETCORE259PATH=%X86_PROGRAMFILES%\Reference Assemblies\Microsoft\FSharp\.NETCore\3.259.4.0
136139
set FSDATATPPATH=%X86_PROGRAMFILES%\Reference Assemblies\Microsoft\FSharp\.NETFramework\v4.0\4.3.0.0\Type Providers
140+
set FSCOREDLLVPREVPATH=%X86_PROGRAMFILES%\Reference Assemblies\Microsoft\FSharp\.NETFramework\v4.0\4.3.1.0
137141

138142
REM == open source logic
139143
if exist "%FSCBinPath%\FSharp.Core.dll" set FSCOREDLLPATH=%FSCBinPath%
@@ -151,6 +155,7 @@ set FSCOREDLLNETCOREPATH=%FSCOREDLLNETCOREPATH%\FSharp.Core.dll
151155
set FSCOREDLLNETCORE78PATH=%FSCOREDLLNETCORE78PATH%\FSharp.Core.dll
152156
set FSCOREDLLNETCORE259PATH=%FSCOREDLLNETCORE259PATH%\FSharp.Core.dll
153157
set FSDATATPPATH=%FSDATATPPATH%\FSharp.Data.TypeProviders.dll
158+
set FSCOREDLLVPREVPATH=%FSCOREDLLVPREVPATH%\FSharp.Core.dll
154159

155160
for /d %%i in (%WINDIR%\Microsoft.NET\Framework\v4.0.?????) do set CORDIR=%%i
156161
set PATH=%PATH%;%CORDIR%
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
open System
2+
open System.IO
3+
open System.Diagnostics
4+
open System.Reflection
5+
6+
module Helpers =
7+
8+
// runs a program, and exits the script if nonzero exit code is encountered
9+
let private run exePath args =
10+
let args = String.concat " " args
11+
let psi = ProcessStartInfo(FileName = exePath, Arguments = args, CreateNoWindow = true, UseShellExecute = false, RedirectStandardError = true)
12+
let p = Process.Start(psi)
13+
match p.WaitForExit(10 * 60 * 1000) with
14+
| false -> eprintfn "Process timed out"; exit 1
15+
| true ->
16+
if p.ExitCode <> 0 then
17+
eprintfn "%s %s" exePath args
18+
eprintfn "%s" (p.StandardError.ReadToEnd())
19+
exit p.ExitCode
20+
21+
let private authorCompile compilerPath runtime source =
22+
run compilerPath ["-a"; "-o:author.dll"; "--noframework"; sprintf "\"-r:%s\"" runtime; source]
23+
24+
let private consumerCompile compilerPath runtime source =
25+
run compilerPath ["-o:consumer.exe"; "--noframework"; sprintf "\"-r:%s\"" runtime; "-r:author.dll"; source]
26+
27+
let private consumerRunFsi fsiPath source =
28+
run fsiPath ["--exec"; source]
29+
30+
// runs the consumer EXE, handling binding redirects automatically
31+
let private consumerRunExe redirectVer =
32+
if File.Exists("consumer.exe.config") then
33+
File.Delete("consumer.exe.config")
34+
35+
let content = File.ReadAllText("consumer.exe.config.txt").Replace("{ver}", redirectVer)
36+
File.WriteAllText("consumer.exe.config", content)
37+
38+
run "consumer.exe" []
39+
40+
/// gets the version of the assembly at the specified path
41+
let getVer dllPath =
42+
let asm = Assembly.ReflectionOnlyLoadFrom(dllPath)
43+
asm.GetName().Version.ToString()
44+
45+
/// runs through the end-to-end scenario of
46+
/// - Author uses [authorComiler] to build DLL targeting [authorRuntime] with source [authorSource]
47+
/// - Consumer uses [consumerCompiler] to build EXE ref'ing above DLL, building EXE targeting [consumerRuntime] with source [consumerSource]
48+
/// - Run the resulting EXE
49+
let testExe authorCompiler authorRuntime consumerCompiler consumerRuntime authorSource consumerSource =
50+
authorCompile authorCompiler authorRuntime authorSource
51+
consumerCompile consumerCompiler consumerRuntime consumerSource
52+
consumerRunExe (getVer consumerRuntime)
53+
54+
/// runs through the end-to-end scenario of
55+
/// - Author uses [authorComiler] to build DLL targeting [authorRuntime] with source [authorSource]
56+
/// - Consumer uses [consumerFsi] to #r above DLL and run script [consumerSource]
57+
let testFsi authorCompiler authorRuntime consumerFsi authorSource consumerSource =
58+
authorCompile authorCompiler authorRuntime authorSource
59+
consumerRunFsi consumerFsi consumerSource
60+
61+
module Test =
62+
let private env s =
63+
match Environment.GetEnvironmentVariable(s) with
64+
| var when not (String.IsNullOrWhiteSpace(var)) -> var
65+
| _ -> failwithf "Required env var %s not defined" s
66+
67+
// paths to vPrevious of fsc.exe, fsi.exe, FSharp.Core.dll
68+
let vPrevCompiler = env "FSCVPREV"
69+
let vPrevFsi = Path.Combine(env "FSCVPREVBINPATH", "fsi.exe")
70+
let vPrevRuntime = env "FSCOREDLLVPREVPATH"
71+
72+
// paths to vCurrent of fsc.exe, fsi.exe, FSharp.Core.dll
73+
let vCurrentCompiler = env "FSC"
74+
let vCurrentFsi = Path.Combine(env "FSCBINPATH", "fsi.exe")
75+
let vCurrentRuntime = env "FSCOREDLLPATH"
76+
77+
let cases =
78+
// compiler/runtime of author | compiler/runtime of consumer
79+
[ 0, Helpers.testExe vPrevCompiler vPrevRuntime vCurrentCompiler vPrevRuntime
80+
1, Helpers.testExe vPrevCompiler vPrevRuntime vCurrentCompiler vCurrentRuntime
81+
2, Helpers.testExe vCurrentCompiler vPrevRuntime vPrevCompiler vPrevRuntime
82+
3, Helpers.testExe vCurrentCompiler vPrevRuntime vCurrentCompiler vPrevRuntime
83+
4, Helpers.testExe vCurrentCompiler vPrevRuntime vCurrentCompiler vCurrentRuntime
84+
5, Helpers.testExe vCurrentCompiler vCurrentRuntime vCurrentCompiler vCurrentRuntime
85+
86+
// compiler/runtime of author | fsi of consumer
87+
6, Helpers.testFsi vPrevCompiler vPrevRuntime vCurrentFsi
88+
7, Helpers.testFsi vCurrentCompiler vPrevRuntime vCurrentFsi
89+
8, Helpers.testFsi vCurrentCompiler vPrevRuntime vPrevFsi
90+
9, Helpers.testFsi vCurrentCompiler vCurrentRuntime vCurrentFsi
91+
]
92+
93+
// parse command line args
94+
// final 'exclusions' arg allows for certain scenarios to be skipped if they are not expected to work
95+
let authorSource, consumerSource, exclusions =
96+
match fsi.CommandLineArgs with
97+
| [| _; arg1; arg2 |] -> arg1, arg2, [| |]
98+
| [| _; arg1; arg2; arg3 |] -> arg1, arg2, (arg3.Split(',') |> Array.map int)
99+
| args ->
100+
eprintfn "Expecting args <author source> <consumer source> [excluded cases], got args %A" args
101+
exit 1
102+
103+
// failsafe to make sure that excluded scenarios are revisited on new versions
104+
// i.e. exclusions valid for vN/vN-1 will probably no longer be needed for vN+1/vN
105+
if not ((Helpers.getVer Test.vCurrentRuntime).StartsWith("4.4.0")) then
106+
eprintfn "Runtime version has changed, review exclusions lists for these tests"
107+
exit 1
108+
109+
Test.cases
110+
|> List.filter (fun (id, _) -> not (Array.contains id exclusions))
111+
|> List.iter (fun (id, testCase) ->
112+
printfn "Case %d" id
113+
testCase authorSource consumerSource
114+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module Foo
2+
3+
type ``one, two, three``() = class end
4+
5+
let X = <@ ``one, two, three``() @>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#if INTERACTIVE
2+
#r "author.dll"
3+
#else
4+
module Test
5+
#endif
6+
7+
printfn "%A" Foo.X
8+
9+
#if INTERACTIVE
10+
#q ;;
11+
#endif
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<configuration>
3+
<runtime>
4+
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
5+
<dependentAssembly>
6+
<assemblyIdentity
7+
name="FSharp.Core"
8+
publicKeyToken="b03f5f7f11d50a3a"
9+
culture="neutral"/>
10+
<bindingRedirect
11+
oldVersion="2.0.0.0-{ver}"
12+
newVersion="{ver}"/>
13+
</dependentAssembly>
14+
</assemblyBinding>
15+
</runtime>
16+
</configuration>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// Every testcase need a sourcefile, so this is a fake one.
2+
// Add some code so that you don't get the 'empty module' warning.
3+
4+
if false then
5+
printfn "Hello, World!"
6+
7+
exit 0

tests/fsharpqa/Source/MultiTargeting/env.lst

+2
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,5 @@ NOMONO SOURCE=E_BadPathToFSharpCore.fsx SCFLAGS=" " ISCFLAGS="--nofra
44

55
# FSharp.Core is checked in for this test to verify a particular error message related to it. It shouldn't be accidentally picked up by other tests since it isn't in the working directory for them
66
NOMONO SOURCE=E_UseBinaryIncompatibleLibrary.fs SCFLAGS=" " ISCFLAGS="--noframework -r ..\\Common\\FSharp.Core.dll" # E_UseBinaryIncompatibleLibrary.fs
7+
8+
SOURCE=dummy.fs POSTCMD="\$FSI_PIPE --nologo --quiet --exec .\\MultiTargetMatrix.fsx QuotedCommaTypeName_author.fs QuotedCommaTypeName_consumer.fsx 0,8" # QuotedCommaTypeName

tests/fsharpqa/Source/test.lst

+1-1
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ Misc01 Libraries\Core\Unchecked
301301

302302
Misc02 Libraries\Portable
303303
Misc02 Misc
304-
Misc02 MultiTargeting
304+
Misc02,Smoke MultiTargeting
305305
Misc02 OCamlCompat
306306
Misc02,CodeGen Optimizations\AssemblyBoundary
307307
Misc02,CodeGen Optimizations\ForLoop

0 commit comments

Comments
 (0)