• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

thoni56 / c-xrefactory / 1800

22 Jun 2026 07:28AM UTC coverage: 85.796% (+0.07%) from 85.723%
1800

push

travis-ci

thoni56
[test][server] Pin: prepareInputFileForRequest picks wrong file under path-hash accident

Suspended Cgreen test describing the bug. prepareInputFileForRequest()
picks the first scheduled file by fileNumber order, where fileNumber is
hash(absolute path). When an unrelated file ends up scheduled alongside
the request file, the wrong one can win depending on which path hashes
lower -- making test_browsing_push_in_unexpanded_macro fail on macOS
while passing on Linux at the same commit.

Exposes the static function as protected so the test can call it. The
test itself is xEnsure-suspended so the suite stays green; un-suspend it
when fixing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

16460 of 19185 relevant lines covered (85.8%)

16396622.56 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

84.9
src/move_function.c
1
#include "move_function.h"
2

3
#include <assert.h>
4
#include <ctype.h>
5
#include <stdbool.h>
6
#include <stdio.h>
7
#include <stdlib.h>
8
#include <string.h>
9

10
#include "commons.h"
11
#include "cxref.h"
12
#include "editor.h"
13
#include "editormarker.h"
14
#include "editorbuffer.h"
15
#include "filetable.h"
16
#include "fileio.h"
17
#include "globals.h"
18
#include "misc.h"
19
#include "options.h"
20
#include "parsing.h"
21
#include "ppc.h"
22
#include "proto.h"
23
#include "protocol.h"
24
#include "refactory.h"
25
#include "reference.h"
26
#include "referenceableitemtable.h"
27
#include "stackmemory.h"
28
#include "undo.h"
29

30

31
static void moveMarkerToTheEndOfDefinitionScope(EditorMarker *marker) {
11✔
32
    int offset;
11✔
33
    offset = marker->offset;
11✔
34
    moveEditorMarkerToNonBlankOrNewline(marker, 1);
11✔
35
    if (marker->offset >= marker->buffer->allocation.bufferSize) {
11✔
36
        return;
37
    }
38
    if (CHAR_ON_MARKER(marker) == '/' && CHAR_AFTER_MARKER(marker) == '/') {
×
39
        if (options.commentMovingMode == CM_NO_COMMENT)
40
            return;
×
41
        moveEditorMarkerToNewline(marker, 1);
×
42
        marker->offset++;
×
43
    } else if (CHAR_ON_MARKER(marker) == '/' && CHAR_AFTER_MARKER(marker) == '*') {
44
        if (options.commentMovingMode == CM_NO_COMMENT)
×
45
            return;
×
46
        marker->offset++;
×
47
        marker->offset++;
×
48
        while (marker->offset < marker->buffer->allocation.bufferSize &&
×
49
               (CHAR_ON_MARKER(marker) != '*' || CHAR_AFTER_MARKER(marker) != '/')) {
50
            marker->offset++;
×
51
        }
×
52
        if (marker->offset < marker->buffer->allocation.bufferSize) {
×
53
            marker->offset++;
54
            marker->offset++;
×
55
        }
×
56
        offset = marker->offset;
×
57
        moveEditorMarkerToNonBlankOrNewline(marker, 1);
×
58
        if (CHAR_ON_MARKER(marker) == '\n')
59
            marker->offset++;
×
60
        else
11✔
61
            marker->offset = offset;
11✔
62
    } else if (CHAR_ON_MARKER(marker) == '\n') {
63
        marker->offset++;
×
64
    } else {
65
        if (options.commentMovingMode == CM_NO_COMMENT)
×
66
            return;
67
        marker->offset = offset;
68
    }
69
}
7✔
70

7✔
71
static MarkerLocationKind markerWRTComment(EditorMarker *marker, int *commentBeginOffset) {
7✔
72
    char *begin, *end, *mms;
7✔
73
    assert(marker->buffer && marker->buffer->allocation.text);
7✔
74
    char *text = marker->buffer->allocation.text;
9✔
75
    end = text + marker->buffer->allocation.bufferSize;
6✔
76
    mms = text + marker->offset;
6✔
77
    while (text < end && text < mms) {
78
        begin = text;
4✔
79
        if (*text == '/' && (text + 1) < end && *(text + 1) == '*') {
35✔
80
            // /**/ comment
81
            text += 2;
4✔
82
            while ((text + 1) < end && !(*text == '*' && *(text + 1) == '/'))
4✔
83
                text++;
4✔
84
            if (text + 1 < end)
3✔
85
                text += 2;
3✔
86
            if (text > mms) {
87
                *commentBeginOffset = begin - marker->buffer->allocation.text;
2✔
88
                return MARKER_IS_IN_STAR_COMMENT;
89
            }
1✔
90
        } else if (*text == '/' && text + 1 < end && *(text + 1) == '/') {
6✔
91
            // // comment
5✔
92
            text += 2;
1✔
93
            while (text < end && *text != '\n')
1✔
94
                text++;
1✔
95
            if (text < end)
1✔
96
                text += 1;
1✔
97
            if (text > mms) {
98
                *commentBeginOffset = begin - marker->buffer->allocation.text;
1✔
99
                return MARKER_IS_IN_SLASH_COMMENT;
100
            }
×
101
        } else if (*text == '"') {
×
102
            // string, pass it removing all inside (also /**/ comments)
×
103
            text++;
×
104
            while (text < end && *text != '"') {
×
105
                text++;
×
106
                if (*text == '\\') {
107
                    text++;
108
                    text++;
×
109
                }
×
110
            }
111
            if (text < end)
1✔
112
                text++;
113
        } else {
114
            text++;
115
        }
116
    }
117
    return MARKER_IS_IN_CODE;
11✔
118
}
11✔
119

120
static void moveMarkerToTheBeginOfDefinitionScope(EditorMarker *marker) {
11✔
121
    int offsetToBeginning;
11✔
122

4✔
123
    int slashedCommentsProcessed = 0;
15✔
124
    int staredCommentsProcessed = 0;
15✔
125
    for (;;) {
15✔
126
        offsetToBeginning = marker->offset;
15✔
127
        marker->offset--;
10✔
128
        moveEditorMarkerToNonBlankOrNewline(marker, -1);
10✔
129
        if (CHAR_ON_MARKER(marker) == '\n') {
130
            offsetToBeginning = marker->offset + 1;
15✔
131
            marker->offset--;
132
        }
7✔
133
        if (options.commentMovingMode == CM_NO_COMMENT)
7✔
134
            break;
7✔
135
        moveEditorMarkerToNonBlank(marker, -1);
7✔
136
        int comBeginOffset;
137
        MarkerLocationKind markerLocationKind = markerWRTComment(marker, &comBeginOffset);
4✔
138
        if (markerLocationKind == MARKER_IS_IN_CODE)
3✔
139
            break;
140
        else if (markerLocationKind == MARKER_IS_IN_STAR_COMMENT) {
3✔
141
            if (options.commentMovingMode == CM_SINGLE_SLASHED)
142
                break;
3✔
143
            if (options.commentMovingMode == CM_ALL_SLASHED)
144
                break;
3✔
145
            if (staredCommentsProcessed > 0 && options.commentMovingMode == CM_SINGLE_STARRED)
146
                break;
147
            if (staredCommentsProcessed > 0 &&
3✔
148
                options.commentMovingMode == CM_SINGLE_SLASHED_AND_STARRED)
3✔
149
                break;
150
            staredCommentsProcessed++;
151
            marker->offset = comBeginOffset;
1✔
152
        }
1✔
153
        // slash comment, skip them all
154
        else if (markerLocationKind == MARKER_IS_IN_SLASH_COMMENT) {
1✔
155
            if (options.commentMovingMode == CM_SINGLE_STARRED)
156
                break;
157
            if (options.commentMovingMode == CM_ALL_STARRED)
158
                break;
159
            if (slashedCommentsProcessed > 0 && options.commentMovingMode == CM_SINGLE_SLASHED)
1✔
160
                break;
1✔
161
            if (slashedCommentsProcessed > 0 &&
162
                options.commentMovingMode == CM_SINGLE_SLASHED_AND_STARRED)
×
163
                break;
×
164
            slashedCommentsProcessed++;
165
            marker->offset = comBeginOffset;
166
        } else {
11✔
167
            warningMessage(ERR_INTERNAL, "A new comment?");
11✔
168
            break;
169
        }
11✔
170
    }
11✔
171
    marker->offset = offsetToBeginning;
11✔
172
}
11✔
173

11✔
174
static EditorMarker *getTargetFromOptions(void) {
175
    EditorMarker *target;
11✔
176
    EditorBuffer *targetBuffer;
177
    int           targetLine;
11✔
178
    char         *targetFileName = strdup(normalizeFileName_static(refactoringOptions.moveTargetFile, cwd));
×
179

11✔
180
    targetBuffer = findOrCreateAndLoadEditorBufferForFile(targetFileName);
11✔
181

11✔
182
    if (targetBuffer == NULL)
11✔
183
        FATAL_ERROR(ERR_ST, "Could not find a buffer for target position", EXIT_FAILURE);
184
    target = newEditorMarker(targetBuffer, 0);
185
    sscanf(refactoringOptions.refactor_target_line, "%d", &targetLine);
9✔
186
    moveEditorMarkerToLineAndColumn(target, targetLine, 0);
187
    return target;
9✔
188
}
9✔
189

9✔
190
static char *extractFunctionSignature(EditorMarker *startMarker, EditorMarker *endMarker) {
191

187✔
192
    int searchOffset = startMarker->offset;
187✔
193
    int braceOffset = -1;
194
    char *text = startMarker->buffer->allocation.text;
195

196
    while (searchOffset < endMarker->offset) {
178✔
197
        if (text[searchOffset] == '{') {
198
            braceOffset = searchOffset;
199
            break;
9✔
200
        }
9✔
201
        searchOffset++;
202
    }
9✔
203

9✔
204
    char *functionSignature = NULL;
9✔
205
    if (braceOffset != -1) {
9✔
206
        /* Extract signature (from start to just before '{') */
207
        int signatureLength = braceOffset - startMarker->offset;
208
        functionSignature = stackMemoryAlloc(signatureLength + 1);
9✔
209
        strncpy(functionSignature, &text[startMarker->offset], signatureLength);
9✔
210
        functionSignature[signatureLength] = '\0';
18✔
211

9✔
212
        /* Trim trailing whitespace from signature */
9✔
213
        int i = signatureLength - 1;
9✔
214
        while (i >= 0
215
               && (functionSignature[i] == ' ' || functionSignature[i] == '\t'
216
                   || functionSignature[i] == '\n' || functionSignature[i] == '\r')) {
217
            functionSignature[i] = '\0';
9✔
218
            i--;
219
        }
220
    }
9✔
221

9✔
222
    return functionSignature;
9✔
223
}
9✔
224

225
static char *findCorrespondingHeaderFile(EditorMarker *target) {
9✔
226
    char *targetFileName = target->buffer->fileName;
227
    char *suffix = getFileSuffix(targetFileName);
9✔
228
    char *headerFileName = NULL;
9✔
229

230
    if (strcmp(suffix, ".c") == 0) {
9✔
231
        /* Build header filename by replacing .c with .h */
9✔
232
        char headerPath[MAX_FILE_NAME_SIZE];
9✔
233
        int baseLength = suffix - targetFileName; /* Length up to the '.' */
234

9✔
235
        strncpy(headerPath, targetFileName, baseLength);
4✔
236
        headerPath[baseLength] = '\0';
9✔
237
        strcat(headerPath, ".h");
238

239
        if (fileExists(headerPath)) {
240
            headerFileName = stackMemoryAlloc(strlen(headerPath) + 1);
9✔
241
            strcpy(headerFileName, headerPath);
242
        }
243
    }
4✔
244

4✔
245
    return headerFileName;
4✔
246
}
247

248
static int findHeaderInsertionPoint(EditorBuffer *headerBuffer) {
28✔
249
    char *text = headerBuffer->allocation.text;
28✔
250
    int size = getSizeOfEditorBuffer(headerBuffer);
251

4✔
252
    /* Search backwards for "\n#endif" pattern */
4✔
253
    for (int i = size - 1; i >= 1; i--) {
8✔
254
        if (text[i-1] == '\n' && text[i] == '#') {
4✔
255
            /* Found newline followed by # - check if it's #endif */
256
            if (i + 5 < size && strncmp(&text[i], "#endif", 6) == 0) {
257
                int offset = i - 1;  /* Start at the '\n' before '#' */
258
                while (offset > 0 && isspace(text[offset-1])) {
259
                    offset--;
260
                }
261
                return offset;
262
            }
×
263
        }
×
264
    }
265

266
    /* Edge case: file starts with #endif (no preceding newline), just insert it first */
267
    if (size >= 6 && strncmp(text, "#endif", 6) == 0) {
268
        return 0;
269
    }
270

4✔
271
    /* No #endif found - insert at end */
272
    return size;
4✔
273
}
4✔
274

4✔
275
static void insertExternDeclaration(char *functionSignature, EditorBuffer *headerBuffer, int offset) {
4✔
276
    /* Build extern declaration: "extern <signature>;\n" */
4✔
277
    char *externDecl =
278
        stackMemoryAlloc(strlen("\nextern ") + strlen(functionSignature) + strlen(";") + 1);
279
    strcpy(externDecl, "\nextern ");
4✔
280
    strcat(externDecl, functionSignature);
4✔
281
    strcat(externDecl, ";");
282

71✔
283
    /* Insert at the beginning of the header file */
48✔
284
    replaceStringInEditorBuffer(headerBuffer, offset, 0, externDecl, strlen(externDecl), &editorUndo);
76✔
285
}
286

287
static bool lookingAtStatic(char *text, int remaining) {
9✔
288
    return remaining >= strlen("static ") && strncmp(text, "static", 6) == 0
9✔
289
        && (text[6] == ' ' || text[6] == '\t');
9✔
290
}
9✔
291

292
static int removeStaticKeywordIfPresent(EditorMarker *startMarker, EditorMarker *point, int size) {
75✔
293
    EditorMarker *searchMarker = newEditorMarker(startMarker->buffer, startMarker->offset);
71✔
294
    EditorMarker *staticMarker = NULL;
71✔
295
    bool foundStatic = false;
296

297
    while (searchMarker->offset < point->offset) {
71✔
298
        char *text = &startMarker->buffer->allocation.text[searchMarker->offset];
5✔
299
        int remaining = point->offset - searchMarker->offset;
5✔
300

5✔
301
        /* Check if we're at "static " (with space or tab after) */
302
        if (lookingAtStatic(text, remaining)) {
66✔
303
            foundStatic = true;
304
            staticMarker = newEditorMarker(startMarker->buffer, searchMarker->offset);
9✔
305
            break;
306
        }
9✔
307
        searchMarker->offset++;
5✔
308
    }
309
    freeEditorMarker(searchMarker);
5✔
310

311
    if (foundStatic) {
5✔
312
        replaceStringInEditorBuffer(staticMarker->buffer, staticMarker->offset, strlen("static "),
313
                                    "", 0, &editorUndo);
314
        freeEditorMarker(staticMarker);
9✔
315
        /* Adjust size since we removed "static " */
316
        size -= strlen("static ");
317
    }
4✔
318

4✔
319
    return size;
4✔
320
}
321

4✔
322
static bool sourceAlreadyIncludesHeader(EditorBuffer *sourceBuffer, char *headerFileName) {
323
    int sourceFileNumber = sourceBuffer->fileNumber;
324
    int headerFileNumber = getFileNumberFromName(headerFileName);
325

326
    if (headerFileNumber == NO_FILE_NUMBER) {
4✔
327
        return false;  /* Header file not in file table yet */
328
    }
329

330
    /* Create search item for the header file */
331
    ReferenceableItem searchItem = makeReferenceableItem(LINK_NAME_INCLUDE_REFS, TypeCppInclude,
4✔
332
                                                         StorageExtern, GlobalScope, VisibilityGlobal,
4✔
333
                                                         headerFileNumber);
334

7✔
335
    /* Look it up in the reference table */
6✔
336
    ReferenceableItem *foundItem;
337
    if (isMemberInReferenceableItemTable(&searchItem, NULL, &foundItem)) {
338
        /* Check if source file appears in the references */
339
        for (Reference *ref = foundItem->references; ref != NULL; ref = ref->next) {
340
            if (ref->position.file == sourceFileNumber && ref->usage == UsageUsed) {
341
                return true;  /* source file already includes header! */
342
            }
343
        }
344
    }
2✔
345

2✔
346
    return false;  /* Include not found */
2✔
347
}
2✔
348

349
static int findIncludeInsertionPoint(EditorBuffer *buffer) {
350
    char *text = buffer->allocation.text;
9,716✔
351
    int size = getSizeOfEditorBuffer(buffer);
352
    int lastIncludeEnd = 0;  /* Offset after last include, or 0 if none found */
9,714✔
353

22✔
354
    /* Scan forward looking for #include directives */
355
    for (int i = 0; i < size - 8; i++) {  /* 8 = strlen("#include") */
22✔
356
        /* Look for newline followed by #include (or start of file) */
294✔
357
        if ((i == 0 || text[i-1] == '\n') && text[i] == '#') {
272✔
358
            if (strncmp(&text[i], "#include", 8) == 0) {
359
                /* Found an include - find the end of this line */
22✔
360
                int j = i + 8;
22✔
361
                while (j < size && text[j] != '\n') {
362
                    j++;
363
                }
364
                if (j < size) {
365
                    lastIncludeEnd = j + 1;  /* Offset after the newline */
366
                }
2✔
367
            }
368
        }
369
    }
370

11✔
371
    return lastIncludeEnd;  /* 0 if no includes found = insert at start */
372
}
11✔
373

11✔
374

11✔
375
static void moveStaticFunctionAndMakeItExtern(EditorMarker *startMarker, EditorMarker *point,
376
                                              EditorMarker *endMarker, EditorMarker *target) {
×
377
    int functionBlockSize = endMarker->offset - startMarker->offset;
×
378
    EditorBuffer *sourceBuffer = startMarker->buffer;
379
    if (target->buffer == startMarker->buffer && target->offset > startMarker->offset &&
380
        target->offset < startMarker->offset + functionBlockSize) {
381
        ppcGenRecord(PPC_INFORMATION, "You can't move something into itself.");
382
        return;
11✔
383
    }
384

11✔
385
    /* Check if function has "static" keyword and remove it when moving between files.
9✔
386
     * When moving within the same file, keep static (visibility doesn't change). */
387
    bool movingBetweenFiles = (startMarker->buffer != target->buffer);
388

389
    if (movingBetweenFiles) {
11✔
390
        functionBlockSize = removeStaticKeywordIfPresent(startMarker, point, functionBlockSize);
11✔
391
    }
392

9✔
393
    /* Extract function signature for extern declaration (before moving) */
394
    char *functionSignature = NULL;
395
    if (movingBetweenFiles) {
396
        /* Find the opening brace to determine where signature ends */
11✔
397
        functionSignature = extractFunctionSignature(startMarker, endMarker);
398
    }
399

11✔
400
    /* Now move the (possibly modified) function block */
11✔
401
    moveBlockInEditorBuffer(startMarker, target, functionBlockSize, &editorUndo);
9✔
402

403
    /* After moving the function, check if we need to add an extern declaration to the header */
404
    char *headerFileName = NULL;
405
    if (movingBetweenFiles) {
11✔
406
        headerFileName = findCorrespondingHeaderFile(target);
4✔
407
    }
4✔
408

4✔
409
    /* Insert extern declaration into header file if we have both header and signature */
410
    if (headerFileName != NULL && functionSignature != NULL) {
411
        EditorBuffer *headerBuffer = findOrCreateAndLoadEditorBufferForFile(headerFileName);
4✔
412
        int insertionOffset = findHeaderInsertionPoint(headerBuffer);
413
        insertExternDeclaration(functionSignature, headerBuffer, insertionOffset);
2✔
414

2✔
415
        /* Check if source file needs to include the header */
2✔
416
        if (!sourceAlreadyIncludesHeader(sourceBuffer, headerFileName)) {
2✔
417
            /* Build the include directive */
2✔
418
            char *baseHeaderName = simpleFileName(headerFileName);
419
            char *includeDirective = stackMemoryAlloc(strlen("#include \"") + strlen(baseHeaderName) + strlen("\"\n") + 1);
420
            strcpy(includeDirective, "#include \"");
2✔
421
            strcat(includeDirective, baseHeaderName);
2✔
422
            strcat(includeDirective, "\"\n");
423

424
            /* Insert at beginning of source file */
425
            int insertionOffset = findIncludeInsertionPoint(sourceBuffer);
426
            replaceStringInEditorBuffer(sourceBuffer, insertionOffset, 0, includeDirective, strlen(includeDirective), &editorUndo);
11✔
427
        }
11✔
428
    }
429
}
11✔
430

×
431
void moveFunction(EditorMarker *point) {
×
432
    EditorMarker *target = getTargetFromOptions();
433

434
    if (!isValidMoveTarget(target)) {
11✔
435
        errorMessage(ERR_ST, "Invalid target place");
436
        return;
11✔
437
    }
×
438

439
    FunctionBoundariesResult bounds = getFunctionBoundaries(point);
440

441
    if (!bounds.found) {
11✔
442
        FATAL_ERROR(ERR_INTERNAL, "Can't find declaration coordinates", EXIT_FAILURE);
11✔
443
    }
11✔
444

11✔
445
    /* Convert positions to markers and adjust for definition scope */
446
    EditorMarker *functionStart = newEditorMarkerForPosition(bounds.functionBegin);
11✔
447
    EditorMarker *functionEnd = newEditorMarkerForPosition(bounds.functionEnd);
448
    moveMarkerToTheBeginOfDefinitionScope(functionStart);
11✔
449
    moveMarkerToTheEndOfDefinitionScope(functionEnd);
450

451
    int lines = countLinesBetweenEditorMarkers(functionStart, functionEnd);
11✔
452

11✔
453
    moveStaticFunctionAndMakeItExtern(functionStart, point, functionEnd, target);
11✔
454

455
    // and generate output
456
    applyWholeRefactoringFromUndo();
457
    ppcGotoMarker(point);
458
    ppcValueRecord(PPC_INDENT, lines, "");
459
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc