diff --git a/components/codetools/basiccodetools.pas b/components/codetools/basiccodetools.pas index 08f71c8cf9..fa6e348ce1 100644 --- a/components/codetools/basiccodetools.pas +++ b/components/codetools/basiccodetools.pas @@ -193,7 +193,7 @@ function DottedIdentifierLength(Identifier: PChar): integer; function GetDottedIdentifier(Identifier: PChar): string; function IsDottedIdentifier(const Identifier: string): boolean; function CompareDottedIdentifiers(Identifier1, Identifier2: PChar): integer; // compares both to maximum dotted identifier -function CompareDottedIdentifiersCaseSens(Identifier1, Identifier2: PChar): integer; +function CompareDottedIdentifiersCase(Identifier1, Identifier2: PChar): integer; // case sensitive CompareDottedIdentifiers function ChompDottedIdentifier(const Identifier: string): string; function SkipDottedIdentifierPart(var Identifier: PChar): boolean; @@ -5341,22 +5341,66 @@ begin end; end; -function CompareDottedIdentifiersCaseSens(Identifier1, Identifier2: PChar): integer; +function CompareDottedIdentifiersCase(Identifier1, Identifier2: PChar): integer; +var + c: Char; begin - if (Identifier1<>nil) then begin - if (Identifier2<>nil) then begin - while (Identifier1[0]=Identifier2[0]) do begin - if (IsDottedIdentChar[Identifier1[0]]) then begin + if (Identifier1<>nil) + and (IsIdentStartChar[Identifier1^] + or ((Identifier1^='&') and IsIdentStartChar[Identifier1[1]])) then + begin + if Identifier1^='&' then inc(Identifier1); + if (Identifier2<>nil) + and (IsIdentStartChar[Identifier2^] + or ((Identifier2^='&') and IsIdentStartChar[Identifier2[1]])) then + begin + if Identifier2^='&' then inc(Identifier2); + while Identifier1^=Identifier2^ do begin + c:=Identifier1^; + if (IsDottedIdentChar[c]) then begin inc(Identifier1); inc(Identifier2); + if c='.' then begin + if Identifier1^='&' then begin + if IsIdentStartChar[Identifier1[1]] then + inc(Identifier1) + else begin + if Identifier2^='&' then + inc(Identifier2); + if IsIdentStartChar[Identifier2^] then + exit(1) // for example 'a.&' 'a.&b' + else + exit(0); // for example 'a.&' 'a.&' + end; + end; + if Identifier2^='&' then begin + if IsIdentStartChar[Identifier2[1]] then + inc(Identifier2) + else + exit(-1); // for example 'a.&b' 'a.&' + end; + if Identifier1^='.' then begin + // '..' + if IsIdentStartChar[Identifier2^] then + exit(1) // for example 'a..' 'a.b' + else + exit(0); // for example 'a..' 'a.1' + end; + if Identifier2^='.' then begin + // '..' + if IsIdentStartChar[Identifier1^] then + exit(-1) // for example 'a.b' 'a..' + else + exit(0); // for example 'a.1' 'a..' + end; + end; end else begin - Result:=0; // for example 'aaA;' 'aAa;' - exit; + exit(0); // for example 'aa;' 'aa;' end; end; - if (IsDottedIdentChar[Identifier1[0]]) then begin - if (IsDottedIdentChar[Identifier2[0]]) then begin - if Identifier1[0]>Identifier2[0] then + if (IsDottedIdentChar[Identifier1^]) then begin + if (IsDottedIdentChar[Identifier2^]) then begin + if Identifier1^>Identifier2^ then Result:=-1 // for example 'aab' 'aaa' else Result:=1; // for example 'aaa' 'aab' @@ -5364,7 +5408,7 @@ begin Result:=-1; // for example 'aaa' 'aa;' end; end else begin - if (IsDottedIdentChar[Identifier2[0]]) then + if (IsDottedIdentChar[Identifier2^]) then Result:=1 // for example 'aa;' 'aaa' else Result:=0; // for example 'aa;' 'aa,' @@ -5373,7 +5417,10 @@ begin Result:=-1; // for example 'aaa' nil end; end else begin - if (Identifier2<>nil) then begin + if (Identifier2<>nil) + and (IsIdentStartChar[Identifier2^] + or ((Identifier2^='&') and IsIdentStartChar[Identifier2[1]])) then + begin Result:=1; // for example nil 'bbb' end else begin Result:=0; // for example nil nil diff --git a/components/codetools/codetoolmanager.pas b/components/codetools/codetoolmanager.pas index ca6c4ad9a0..b0cd6c3a5d 100644 --- a/components/codetools/codetoolmanager.pas +++ b/components/codetools/codetoolmanager.pas @@ -3045,7 +3045,7 @@ begin end; // change if needed if DottedIdents then - i:=CompareDottedIdentifiersCaseSens(@Code.Source[IdentStartPos],PChar(Pointer(NewIdentifier))) + i:=CompareDottedIdentifiersCase(@Code.Source[IdentStartPos],PChar(Pointer(NewIdentifier))) else i:=CompareIdentifiersCaseSensitive(@Code.Source[IdentStartPos],PChar(Pointer(NewIdentifier))); if i<>0 then begin diff --git a/components/codetools/tests/testbasiccodetools.pas b/components/codetools/tests/testbasiccodetools.pas index 8ce43dfcfd..85c529b282 100644 --- a/components/codetools/tests/testbasiccodetools.pas +++ b/components/codetools/tests/testbasiccodetools.pas @@ -52,6 +52,7 @@ type procedure TestReadNextPascalAtom; procedure TestCompareIdentifiers; procedure TestCompareDottedIdentifiers; + procedure TestCompareDottedIdentifiersCase; // FileProcs procedure TestDateToCfgStr; procedure TestFilenameIsMatching; @@ -496,6 +497,8 @@ begin t('&a','&;',-1); // dotted + t('a','a.',1); // compares 'a' and 'a.' + t('a','a&',0); // compares 'a' and 'a' t('a','a.b',1); t('a.b','a.b',0); t('a.b','a.b.c',1); @@ -513,6 +516,94 @@ begin t('a.&','a.&1',0); // compares 'a.' and 'a.' end; +procedure TTestBasicCodeTools.TestCompareDottedIdentifiersCase; + + function GetStr(A: PChar): string; + begin + if A=nil then + Result:='nil' + else if A^=#0 then + Result:='#0' + else + Result:='"'+A+'"'; + end; + + procedure Test(A, B: PChar; Expected: integer); + var + Actual: Integer; + AmpA, AmpB: string; + begin + Actual:=CompareDottedIdentifiersCase(A,B); + if Actual<>Expected then + Fail('A='+GetStr(A)+' B='+GetStr(B)+', expected '+dbgs(Expected)+', but got '+dbgs(Actual)); + + if (A<>nil) and (IsIdentStartChar[A^]) then begin + AmpA:='&'+A; + Test(PChar(AmpA),B,Expected); + if (B<>nil) and (IsIdentStartChar[B^]) then begin + AmpB:='&'+B; + Test(PChar(AmpA),PChar(AmpB),Expected); + end; + end; + end; + + procedure t(A, B: PChar; Expected: integer); + begin + Test(A,B,Expected); + Test(B,A,-Expected); + end; + +begin + t(nil,nil,0); + t(nil,#0,0); + t(nil,#1,0); + t(#0,#0,0); + t(#0,#1,0); + t(#1,#2,0); + t('a',nil,-1); + t('a',#0,-1); + t('a','a',0); + t('a','A',-1); + t('aa','aa',0); + t('aa','a',-1); + t('ab','a',-1); + t('ab','a;',-1); + t('ab','aa',-1); + t('ab','aaa',-1); + t('ab;','ab',0); + t('ab;','ab,',0); + t('aaa;','aaa',0); + t('i','i',0); + t('a',';',-1); + t('1','2',0); + t(',',',',0); + t(',',';',0); + t('&',nil,0); + t('&',#0,0); + t('&','&',0); + t('&a','&',-1); + t('&a','&;',-1); + + // dotted + t('a','a.',1); // compares 'a' and 'a.' + t('a','a&',0); // compares 'a' and 'a' + t('a','a.b',1); + t('a.b','a.b',0); + t('a.b','a.b.c',1); + t('a.b','a.b',0); + t('a.&b','a.b',0); + t('a..b','a..b',0); // compares only A. + t('a..&b','a..b',0); // compares only A. + t('a..b','a..c',0); // compares only A. + t('a..&b','a..c',0); // compares only A. + t('a.&b','a.c',1); + t('a.&b','a.&c',1); + t('a.&b','a.&b',0); + t('a.&','a.c',1); // compares 'a.' and 'a.c' + t('a.&','a.&c',1); // compares 'a.' and 'a.&c' + t('a.&','a.&1',0); // compares 'a.' and 'a.' +end; + procedure TTestBasicCodeTools.TestDateToCfgStr; procedure t(const Date: TDateTime; const aFormat, Expected: string);