Skip to content

Commit 540c644

Browse files
authored
Merge pull request #2751 from Widthdom/fix-issue1438-2004-2005
2 parents 7a13258 + b99f4ea commit 540c644

9 files changed

Lines changed: 584 additions & 6 deletions

File tree

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
category: fixed
3+
issues:
4+
- 1438
5+
affected:
6+
- src/CodeIndex/Indexer/References/ReferenceExtractor.Preparation.cs
7+
- src/CodeIndex/Indexer/References/ReferenceExtractor.TypeReferences.cs
8+
- tests/CodeIndex.Tests/ReferenceExtractorTests.cs
9+
---
10+
11+
## English
12+
13+
- **Python multi-line f-strings no longer emit references from literal text (#1438)** — triple-quoted f-string bodies are masked across physical lines while interpolation expressions still contribute real reference edges.
14+
15+
## 日本語
16+
17+
- **Python の複数行 f-string がリテラル本文から参照を出さなくなりました (#1438)** — 三重引用符の f-string 本文を物理行をまたいでマスクしつつ、補間式内の実参照は引き続き抽出します。
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
category: fixed
3+
issues:
4+
- 2004
5+
affected:
6+
- src/CodeIndex/Indexer/Symbols/SymbolExtractor.Lisp.cs
7+
- tests/CodeIndex.Tests/SymbolExtractorTests.cs
8+
---
9+
10+
## English
11+
12+
- **Lisp reader macros no longer create phantom definitions (#2004)** — quoted and quasiquoted forms are excluded from symbol definition extraction so macro templates do not appear as real functions.
13+
14+
## 日本語
15+
16+
- **Lisp の reader macro が phantom 定義を作らなくなりました (#2004)** — quote / quasiquote されたフォームをシンボル定義抽出から除外し、マクロテンプレートが実関数として現れないようにしました。
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
category: fixed
3+
issues:
4+
- 2005
5+
affected:
6+
- src/CodeIndex/Indexer/Symbols/SymbolExtractor.Perl.cs
7+
- tests/CodeIndex.Tests/SymbolExtractorTests.cs
8+
---
9+
10+
## English
11+
12+
- **Perl hash constant keys are normalized and deduplicated (#2005)** — quoted keys are trimmed, escape-decoded, Unicode-normalized, and deduplicated against equivalent bareword constants.
13+
14+
## 日本語
15+
16+
- **Perl の hash constant キーを正規化して重複排除するようになりました (#2005)** — quoted key を trim・escape decode・Unicode 正規化し、等価な bareword constant と重複しないようにしました。

src/CodeIndex/Indexer/References/ReferenceExtractor.Preparation.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,9 @@ private static bool TryPrepareReferenceLines(
8989
: UsesCStyleBlockComments(language)
9090
? MaskCStyleBlockCommentLines(language, structuralLines)
9191
: structuralLines;
92+
if (language == "python")
93+
referenceStructuralLines = MaskPythonFStrings(referenceStructuralLines);
94+
9295
var preparedLines = new string[lines.Length];
9396
for (var pi = 0; pi < lines.Length; pi++)
9497
preparedLines[pi] = PrepareLine(language, referenceStructuralLines[pi]);

src/CodeIndex/Indexer/References/ReferenceExtractor.TypeReferences.cs

Lines changed: 263 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2560,9 +2560,7 @@ private static string ReplaceRegexMatchesWithSpaces(Regex regex, string input)
25602560

25612561
private static string PrepareLine(string lang, string line)
25622562
{
2563-
var result = lang == "python"
2564-
? MaskPythonSingleLineFStrings(line)
2565-
: line;
2563+
var result = line;
25662564
if (lang == "rust")
25672565
result = MaskRustLifetimeTokens(result);
25682566
if (lang != "cobol")
@@ -3188,6 +3186,211 @@ private static string MaskPythonSingleLineFStrings(string line)
31883186
return new string(masked);
31893187
}
31903188

3189+
private static string[] MaskPythonFStrings(IReadOnlyList<string> lines)
3190+
{
3191+
var result = new string[lines.Count];
3192+
for (var lineIndex = 0; lineIndex < lines.Count; lineIndex++)
3193+
result[lineIndex] = lines[lineIndex];
3194+
3195+
for (var lineIndex = 0; lineIndex < result.Length; lineIndex++)
3196+
{
3197+
var line = result[lineIndex];
3198+
if (line.IndexOf('f') < 0 && line.IndexOf('F') < 0)
3199+
continue;
3200+
3201+
var chars = line.ToCharArray();
3202+
var changed = false;
3203+
for (var column = 0; column < line.Length; column++)
3204+
{
3205+
if (!TryOpenPythonString(line, column, out var prefixLength, out var quoteChar, out var isRaw, out var isFString, out var isTripleQuoted))
3206+
continue;
3207+
3208+
if (!isFString)
3209+
{
3210+
column += prefixLength;
3211+
continue;
3212+
}
3213+
3214+
if (!isTripleQuoted)
3215+
{
3216+
result[lineIndex] = MaskPythonSingleLineFStrings(line);
3217+
chars = result[lineIndex].ToCharArray();
3218+
changed = true;
3219+
break;
3220+
}
3221+
3222+
MaskPythonTripleQuotedFString(result, lineIndex, column, prefixLength, quoteChar, isRaw, out var endLineIndex, out var endColumn);
3223+
lineIndex = endLineIndex;
3224+
line = result[lineIndex];
3225+
chars = line.ToCharArray();
3226+
column = endColumn;
3227+
changed = true;
3228+
}
3229+
3230+
if (changed)
3231+
result[lineIndex] = new string(chars);
3232+
}
3233+
3234+
return result;
3235+
}
3236+
3237+
private static void MaskPythonTripleQuotedFString(
3238+
string[] lines,
3239+
int startLineIndex,
3240+
int startColumn,
3241+
int prefixLength,
3242+
char quoteChar,
3243+
bool isRaw,
3244+
out int endLineIndex,
3245+
out int endColumn)
3246+
{
3247+
var lineIndex = startLineIndex;
3248+
var column = startColumn;
3249+
var inExpression = false;
3250+
var inExpressionString = false;
3251+
var expressionDepth = 0;
3252+
var expressionStringQuote = '\0';
3253+
var expressionStringTripleQuoted = false;
3254+
3255+
endLineIndex = startLineIndex;
3256+
endColumn = startColumn;
3257+
3258+
while (lineIndex < lines.Length)
3259+
{
3260+
var line = lines[lineIndex];
3261+
var chars = line.ToCharArray();
3262+
if (lineIndex == startLineIndex)
3263+
{
3264+
ReplaceWithSpaces(chars, startColumn, prefixLength + 3);
3265+
column = startColumn + prefixLength + 3;
3266+
}
3267+
else
3268+
{
3269+
column = 0;
3270+
}
3271+
3272+
while (column < line.Length)
3273+
{
3274+
if (!inExpression)
3275+
{
3276+
if (!isRaw && line[column] == '\\' && column + 1 < line.Length)
3277+
{
3278+
ReplaceWithSpaces(chars, column, 2);
3279+
column += 2;
3280+
continue;
3281+
}
3282+
3283+
if (line[column] == '{' && column + 1 < line.Length && line[column + 1] == '{')
3284+
{
3285+
ReplaceWithSpaces(chars, column, 2);
3286+
column += 2;
3287+
continue;
3288+
}
3289+
3290+
if (line[column] == '}' && column + 1 < line.Length && line[column + 1] == '}')
3291+
{
3292+
ReplaceWithSpaces(chars, column, 2);
3293+
column += 2;
3294+
continue;
3295+
}
3296+
3297+
if (line[column] == '{')
3298+
{
3299+
chars[column++] = ' ';
3300+
inExpression = true;
3301+
expressionDepth = 1;
3302+
continue;
3303+
}
3304+
3305+
if (column + 2 < line.Length
3306+
&& line[column] == quoteChar
3307+
&& line[column + 1] == quoteChar
3308+
&& line[column + 2] == quoteChar)
3309+
{
3310+
ReplaceWithSpaces(chars, column, 3);
3311+
lines[lineIndex] = new string(chars);
3312+
endLineIndex = lineIndex;
3313+
endColumn = column + 2;
3314+
return;
3315+
}
3316+
3317+
chars[column++] = ' ';
3318+
continue;
3319+
}
3320+
3321+
if (inExpressionString)
3322+
{
3323+
if (line[column] == '\\' && column + 1 < line.Length)
3324+
{
3325+
column += 2;
3326+
continue;
3327+
}
3328+
3329+
if (expressionStringTripleQuoted)
3330+
{
3331+
if (column + 2 < line.Length
3332+
&& line[column] == expressionStringQuote
3333+
&& line[column + 1] == expressionStringQuote
3334+
&& line[column + 2] == expressionStringQuote)
3335+
{
3336+
column += 3;
3337+
inExpressionString = false;
3338+
continue;
3339+
}
3340+
3341+
column++;
3342+
continue;
3343+
}
3344+
3345+
if (line[column] == expressionStringQuote)
3346+
{
3347+
column++;
3348+
inExpressionString = false;
3349+
continue;
3350+
}
3351+
3352+
column++;
3353+
continue;
3354+
}
3355+
3356+
if (line[column] == '\'' || line[column] == '"')
3357+
{
3358+
expressionStringQuote = line[column];
3359+
expressionStringTripleQuoted = column + 2 < line.Length
3360+
&& line[column + 1] == expressionStringQuote
3361+
&& line[column + 2] == expressionStringQuote;
3362+
column += expressionStringTripleQuoted ? 3 : 1;
3363+
inExpressionString = true;
3364+
continue;
3365+
}
3366+
3367+
if (line[column] == '{')
3368+
{
3369+
expressionDepth++;
3370+
column++;
3371+
continue;
3372+
}
3373+
3374+
if (line[column] == '}')
3375+
{
3376+
expressionDepth--;
3377+
chars[column++] = ' ';
3378+
if (expressionDepth == 0)
3379+
inExpression = false;
3380+
continue;
3381+
}
3382+
3383+
column++;
3384+
}
3385+
3386+
lines[lineIndex] = new string(chars);
3387+
lineIndex++;
3388+
}
3389+
3390+
endLineIndex = Math.Max(startLineIndex, lines.Length - 1);
3391+
endColumn = 0;
3392+
}
3393+
31913394
private static void ReplaceWithSpaces(char[] buffer, int start, int length)
31923395
{
31933396
for (var i = start; i < start + length && i < buffer.Length; i++)
@@ -3235,6 +3438,63 @@ private static bool TryOpenPythonSingleLineString(
32353438
return true;
32363439
}
32373440

3441+
private static bool TryOpenPythonString(
3442+
string line,
3443+
int startIndex,
3444+
out int prefixLength,
3445+
out char quoteChar,
3446+
out bool isRaw,
3447+
out bool isFString,
3448+
out bool isTripleQuoted)
3449+
{
3450+
isTripleQuoted = false;
3451+
if (!TryOpenPythonSingleOrTripleString(line, startIndex, out prefixLength, out quoteChar, out isRaw, out isFString, out isTripleQuoted))
3452+
return false;
3453+
return true;
3454+
}
3455+
3456+
private static bool TryOpenPythonSingleOrTripleString(
3457+
string line,
3458+
int startIndex,
3459+
out int prefixLength,
3460+
out char quoteChar,
3461+
out bool isRaw,
3462+
out bool isFString,
3463+
out bool isTripleQuoted)
3464+
{
3465+
prefixLength = 0;
3466+
quoteChar = '\0';
3467+
isRaw = false;
3468+
isFString = false;
3469+
isTripleQuoted = false;
3470+
3471+
if (startIndex < 0 || startIndex >= line.Length)
3472+
return false;
3473+
3474+
if (startIndex > 0 && IsIdentifierChar(line[startIndex - 1]))
3475+
return false;
3476+
3477+
var p = startIndex;
3478+
var prefixChars = 0;
3479+
while (p < line.Length && prefixChars < 2 && IsPythonStringPrefixChar(line[p]))
3480+
{
3481+
if (line[p] is 'r' or 'R')
3482+
isRaw = true;
3483+
if (line[p] is 'f' or 'F')
3484+
isFString = true;
3485+
p++;
3486+
prefixChars++;
3487+
}
3488+
3489+
if (p >= line.Length || (line[p] != '\'' && line[p] != '"'))
3490+
return false;
3491+
3492+
prefixLength = p - startIndex;
3493+
quoteChar = line[p];
3494+
isTripleQuoted = p + 2 < line.Length && line[p + 1] == quoteChar && line[p + 2] == quoteChar;
3495+
return true;
3496+
}
3497+
32383498
private static bool IsIgnoredCallName(string language, string name)
32393499
{
32403500
if (LanguageSpecificCallNameKeeps.TryGetValue(language, out var languageSpecificKeepNames)

0 commit comments

Comments
 (0)