Skip to content

Commit 05c9f8b

Browse files
authored
Ensure that scripts without the notion of upper/lower case can create du identifiers (#9199)
* Ensure that scripts without the notion of upper/lower case can create du identifiers * Feedback and improvements * feedback * correct comment
1 parent c26a881 commit 05c9f8b

6 files changed

Lines changed: 33 additions & 20 deletions

File tree

src/absil/illib.fs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -503,9 +503,20 @@ module String =
503503
let uppercase (s: string) =
504504
s.ToUpperInvariant()
505505

506-
let isUpper (s: string) =
507-
s.Length >= 1 && Char.IsUpper s.[0] && not (Char.IsLower s.[0])
508-
506+
// Scripts that distinguish between upper and lower case (bicameral) DU Discriminators and Active Pattern identifiers are required to start with an upper case character.
507+
// For valid identifiers where the case of the identifier can not be determined because there is no upper and lower case we will allow DU Discriminators and upper case characters
508+
// to be used. This means that developers using unicameral scripts such as hindi, are not required to prefix these identifiers with an Upper case latin character.
509+
//
510+
let isLeadingIdentifierCharacterUpperCase (s:string) =
511+
let isUpperCaseCharacter c =
512+
// if IsUpper and IsLower return the same value, then we can't tell if it's upper or lower case, so ensure it is a letter
513+
// otherwise it is bicameral, so must be upper case
514+
let isUpper = Char.IsUpper c
515+
if isUpper = Char.IsLower c then Char.IsLetter c
516+
else isUpper
517+
518+
s.Length >= 1 && isUpperCaseCharacter s.[0]
519+
509520
let capitalize (s: string) =
510521
if s.Length = 0 then s
511522
else uppercase s.[0..0] + s.[ 1.. s.Length - 1 ]

src/fsharp/TypeChecker.fs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5233,7 +5233,7 @@ and TcPatBindingName cenv env id ty isMemberThis vis1 topValData (inlineFlag, de
52335233
let name = id.idText
52345234
match values.TryGetValue name with
52355235
| true, value ->
5236-
if not (String.IsNullOrEmpty name) && Char.IsLower(name.[0]) then
5236+
if not (String.IsNullOrEmpty name) && not (String.isLeadingIdentifierCharacterUpperCase name) then
52375237
match env.eNameResEnv.ePatItems.TryGetValue name with
52385238
| true, Item.Value vref when vref.LiteralValue.IsSome ->
52395239
warning(Error(FSComp.SR.checkLowercaseLiteralBindingInPattern name, id.idRange))
@@ -12775,7 +12775,7 @@ module TcRecdUnionAndEnumDeclarations = begin
1277512775
errorR(Error(FSComp.SR.tcUnionCaseNameConflictsWithGeneratedType(name, "Tags"), id.idRange))
1277612776

1277712777
CheckNamespaceModuleOrTypeName cenv.g id
12778-
if not (String.isUpper name) && name <> opNameCons && name <> opNameNil then
12778+
if not (String.isLeadingIdentifierCharacterUpperCase name) && name <> opNameCons && name <> opNameNil then
1277912779
errorR(NotUpperCaseConstructor(id.idRange))
1278012780

1278112781
let ValidateFieldNames (synFields: SynField list, tastFields: RecdField list) =
@@ -15174,7 +15174,7 @@ module TcExceptionDeclarations =
1517415174

1517515175
let TcExnDefnCore_Phase1A cenv env parent (SynExceptionDefnRepr(Attributes synAttrs, UnionCase(_, id, _, _, _, _), _, doc, vis, m)) =
1517615176
let attrs = TcAttributes cenv env AttributeTargets.ExnDecl synAttrs
15177-
if not (String.isUpper id.idText) then errorR(NotUpperCaseConstructor m)
15177+
if not (String.isLeadingIdentifierCharacterUpperCase id.idText) then errorR(NotUpperCaseConstructor m)
1517815178
let vis, cpath = ComputeAccessAndCompPath env None m vis None parent
1517915179
let vis = TcRecdUnionAndEnumDeclarations.CombineReprAccess parent vis
1518015180
CheckForDuplicateConcreteType env (id.idText + "Exception") id.idRange

src/fsharp/pars.fsy

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3050,7 +3050,7 @@ atomicPattern:
30503050

30513051
| atomicPatternLongIdent %prec prec_atompat_pathop
30523052
{ let vis, lidwd = $1
3053-
if not (isNilOrSingleton lidwd.Lid) || (let c = (List.head lidwd.Lid).idText.[0] in Char.IsUpper(c) && not (Char.IsLower c))
3053+
if not (isNilOrSingleton lidwd.Lid) || String.isLeadingIdentifierCharacterUpperCase (List.head lidwd.Lid).idText
30543054
then mkSynPatMaybeVar lidwd vis (lhs parseState)
30553055
else mkSynPatVar vis (List.head lidwd.Lid) }
30563056

@@ -5293,8 +5293,8 @@ operatorName:
52935293

52945294
/* One part of an active pattern name */
52955295
activePatternCaseName:
5296-
| IDENT
5297-
{ if not (String.isUpper $1) then reportParseErrorAt (rhs parseState 1) (FSComp.SR.parsActivePatternCaseMustBeginWithUpperCase());
5296+
| IDENT
5297+
{ if not (String.isLeadingIdentifierCharacterUpperCase _1) then reportParseErrorAt (rhs parseState 1) (FSComp.SR.parsActivePatternCaseMustBeginWithUpperCase());
52985298
if ($1.IndexOf('|') <> -1) then reportParseErrorAt (rhs parseState 1) (FSComp.SR.parsActivePatternCaseContainsPipe());
52995299
$1 }
53005300

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
// #Regression #Conformance #PatternMatching #ActivePatterns
22
// Verify error if Active Patterns do not start with an upper case letter
3-
//<Expects id="FS0623" status="error" span="(11,7)">Active pattern case identifiers must begin with an uppercase letter</Expects>
4-
//<Expects id="FS0623" status="error" span="(11,16)">Active pattern case identifiers must begin with an uppercase letter</Expects>
53
//<Expects id="FS0623" status="error" span="(12,7)">Active pattern case identifiers must begin with an uppercase letter</Expects>
6-
//<Expects id="FS0623" status="error" span="(13,10)">Active pattern case identifiers must begin with an uppercase letter</Expects>
7-
//<Expects id="FS0623" status="error" span="(14,7)">Active pattern case identifiers must begin with an uppercase letter</Expects>
8-
//<Expects id="FS0624" status="error" span="(15,7)">The '\|' character is not permitted in active pattern case identifiers</Expects>
9-
//<Expects id="FS0624" status="error" span="(16,9)">The '\|' character is not permitted in active pattern case identifiers</Expects>
4+
//<Expects id="FS0623" status="error" span="(12,16)">Active pattern case identifiers must begin with an uppercase letter</Expects>
5+
//<Expects id="FS0623" status="error" span="(13,7)">Active pattern case identifiers must begin with an uppercase letter</Expects>
6+
//<Expects id="FS0623" status="error" span="(14,10)">Active pattern case identifiers must begin with an uppercase letter</Expects>
7+
//<Expects id="FS0623" status="error" span="(15,7)">Active pattern case identifiers must begin with an uppercase letter</Expects>
8+
//<Expects id="FS0624" status="error" span="(16,7)">The '\|' character is not permitted in active pattern case identifiers</Expects>
9+
//<Expects id="FS0624" status="error" span="(17,9)">The '\|' character is not permitted in active pattern case identifiers</Expects>
10+
//<Expects id="FS0623" status="error" span="(18,7)">Active pattern case identifiers must begin with an uppercase letter</Expects>
1011

1112
let (|positive|negative|) n = if n < 0 then positive else negative
1213
let (|`` A``|) (x:int) = x
1314
let (|B1|``+B2``|) (x:int) = if x = 0 then OneA else ``+B2``
1415
let (|`` C``|_|) (x:int) = if x = 0 then Some(x) else None
1516
let (|``D|E``|F|) (x:int) = if x = 0 then D elif x = 1 then E else F
1617
let (|G|``H||I``|) (x:int) = if x = 0 then G elif x = 1 then H else ``|I``
18+
let (|_J|) (x:int) = _J
1719

1820
exit 1

tests/fsharpqa/Source/Conformance/PatternMatching/Named/discUnion01.fs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,17 @@
66
type Foo =
77
| A of int
88
| B of string * int
9-
9+
1010
let test x =
1111
match x with
1212
| A(1) | B(_,1) -> 1
1313
| A(2) | B(_,2) -> 2
1414
| B(_, _) -> -1
1515
| A(_) -> -2
16-
16+
1717
if test (A(1)) <> 1 then exit 1
1818
if test (B("",1)) <> 1 then exit 1
19-
19+
2020
if test (A(2)) <> 2 then exit 1
2121
if test (B("",2)) <> 2 then exit 1
2222

tests/fsharpqa/Source/Globalization/Hindi.fs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ module पिछले =
1414

1515
// DU
1616
type ख़तरxनाक =
17-
| Uअलगाववादी // There's no uppercase/lowercase in Hindi, so I'm adding a latin char
18-
| Aमिलती of ख़तरनाक
17+
| अलगाववादी // There's no uppercase/lowercase in Hindi, ensure that a Hindi character will suffice to start the DU case name
18+
| मिलती of ख़तरनाक
1919
| X
2020

2121
// Record

0 commit comments

Comments
 (0)