Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -385,9 +385,9 @@ async function parseLineAsTool(
// - any spaces/tabs before the tool invocation
// - with or without path (relative -to the makefile location- or full)
// - with or without extension (windows only)
// - with or without quotes
// - with or without quotes (single or double, to support mingw-make)
// - must have at least one space or tab after the tool invocation
let regexpStr: string = '^[\\s\\"]*(.*?)(';
let regexpStr: string = "^[\\s\\\"']*(.*?)(";
if (process.platform === "win32") {
regexpStr += versionedToolNames.join("\\.exe|");

Expand All @@ -399,7 +399,7 @@ async function parseLineAsTool(
regexpStr += "|";
}

regexpStr += versionedToolNames.join("|") + ')(\\s|\\"\\s)(.*)$';
regexpStr += versionedToolNames.join("|") + ")(\\s|[\\\"']\\s)(.*)$";

let regexp: RegExp = RegExp(regexpStr, "mg");
let match: RegExpExecArray | null = regexp.exec(line);
Expand Down
106 changes: 106 additions & 0 deletions src/test/fakeSuite/extension.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,112 @@ suite("Unit testing replacing characters in and outside of quotes", () => {
});
});

// Unit test for the regex pattern used in parseLineAsTool to support
// both single and double quotes in tool invocations (issue #739: mingw-make support)
suite("Unit testing tool invocation regex pattern for single and double quotes", () => {
suiteSetup(async function (this: Mocha.Context) {
this.timeout(100000);
});

setup(async function (this: Mocha.Context) {
this.timeout(100000);
});

// Helper to build the regex pattern similar to parseLineAsTool in parser.ts
function buildToolInvocationRegex(toolNames: string[]): RegExp {
const prefixRegex = "(([a-zA-Z0-9-_.]*-)*";
const suffixRegex = "(-[a-zA-Z0-9-_.]*)*)";
const versionedToolNames = toolNames.map(
(t) => `${prefixRegex}${t}${suffixRegex}`
);

// Pattern from parser.ts that supports both single and double quotes
let regexpStr = "^[\\s\\\"']*(.*?)(";
regexpStr += versionedToolNames.join("|") + ")(\\s|[\\\"']\\s)(.*)$";

return new RegExp(regexpStr, "mg");
}

test("Test tool invocation with double quotes (original behavior)", () => {
const regex = buildToolInvocationRegex(["gcc", "cc", "clang"]);
const testCases = [
{ input: 'gcc -c main.c', shouldMatch: true },
{ input: '"gcc" -c main.c', shouldMatch: true },
{ input: '"/usr/bin/gcc" -c main.c', shouldMatch: true },
];

for (const testCase of testCases) {
regex.lastIndex = 0;
const match = regex.exec(testCase.input);
expect(match !== null).to.equal(
testCase.shouldMatch,
`Expected "${testCase.input}" to ${testCase.shouldMatch ? "match" : "not match"}`
);
}
});

test("Test tool invocation with single quotes (mingw-make style)", () => {
const regex = buildToolInvocationRegex(["gcc", "cc", "clang"]);
const testCases = [
{ input: "'gcc' -c main.c", shouldMatch: true },
{ input: "'/usr/bin/gcc' -c main.c", shouldMatch: true },
{ input: "'clang' -c main.cpp", shouldMatch: true },
];

for (const testCase of testCases) {
regex.lastIndex = 0;
const match = regex.exec(testCase.input);
expect(match !== null).to.equal(
testCase.shouldMatch,
`Expected "${testCase.input}" to ${testCase.shouldMatch ? "match" : "not match"}`
);
}
});

test("Test tool invocation without quotes", () => {
const regex = buildToolInvocationRegex(["gcc", "cc", "clang"]);
const testCases = [
{ input: "gcc -c main.c", shouldMatch: true },
{ input: "/usr/bin/gcc -c main.c", shouldMatch: true },
{ input: "clang -c main.cpp", shouldMatch: true },
{ input: "cc -c test.c", shouldMatch: true },
];

for (const testCase of testCases) {
regex.lastIndex = 0;
const match = regex.exec(testCase.input);
expect(match !== null).to.equal(
testCase.shouldMatch,
`Expected "${testCase.input}" to ${testCase.shouldMatch ? "match" : "not match"}`
);
}
});

test("Test tool path extraction with various quote styles", () => {
const regex = buildToolInvocationRegex(["gcc"]);
const testCases = [
{ input: "/usr/bin/gcc -c main.c", expectedPath: "/usr/bin/" },
{ input: '"/usr/bin/gcc" -c main.c', expectedPath: "/usr/bin/" },
{ input: "'/usr/bin/gcc' -c main.c", expectedPath: "/usr/bin/" },
{ input: "gcc -c main.c", expectedPath: "" },
{ input: "'gcc' -c main.c", expectedPath: "" },
{ input: '"gcc" -c main.c', expectedPath: "" },
];

for (const testCase of testCases) {
regex.lastIndex = 0;
const match = regex.exec(testCase.input);
expect(match).to.not.be.null;
if (match) {
expect(match[1]).to.equal(
testCase.expectedPath,
`Expected path "${testCase.expectedPath}" for input "${testCase.input}", got "${match[1]}"`
);
}
}
});
});

// TODO: refactor initialization and cleanup of each test
suite("Fake dryrun parsing", () => {
suiteSetup(async function (this: Mocha.Context) {
Expand Down