Change:
While reviewing some code in I noticed potential issues: and Will wrongly stop the loop when the result is a even when that result resolves to . Would be good to have a test case for this too.
I think you the support for for the expression matching via , do we really still need this? I feel this makes glob matching overly complicated. Checking on usages, I think search component uses it to lazily resolve children...
PR:
Are you suggesting to resolve children before doing the glob matcher? It would be a lot faster to avoid matching children until you need them. I don't like the flexible typing of this though. It should be explicitly sync or not.

Discussion:
} const resultExpression: ParsedStringPattern = function (path: string, basename?: string) { <del> for (let i = 0, n = parsedPatterns.length; i < n; i++) { </del> <ins> let resultPromises: Promise<string | null>[] | undefined = undefined; </ins> <del> // Check if pattern matches path </del> <ins> for (let i = 0, n = parsedPatterns.length; i < n; i++) { </ins> const result = parsedPatterns[i](path, basename); <del> if (result) { return result; </del> <ins> if (typeof result === 'string') { return result; // immediately return as soon as the first expression matches } // If the result is a promise, we have to keep it for // later processing and await the result properly. if (isThenable(result)) { if (!resultPromises) { resultPromises = []; } resultPromises.push(result); </ins> } } <ins> // With result promises, we have to loop over each and // await the result before we can return any result. if (resultPromises) { return (async () => { for (const resultPromise of resultPromises) { const result = await resultPromise; if (typeof result === 'string') { return result; } } return null; })(); } </ins> return null; };

const resultExpression: ParsedStringPattern = function (path: string, base?: string, hasSibling?: (name: string) => boolean | Promise<boolean>) { let name: string | undefined = undefined; <ins> let resultPromises: Promise<string | null>[] | undefined = undefined; </ins> for (let i = 0, n = parsedPatterns.length; i < n; i++) {

} const result = parsedPattern(path, base, name, hasSibling); <del> if (result) { return result; </del> <ins> if (typeof result === 'string') { return result; // immediately return as soon as the first expression matches } // If the result is a promise, we have to keep it for // later processing and await the result properly. if (isThenable(result)) { if (!resultPromises) { resultPromises = []; } resultPromises.push(result); </ins> } } <ins> // With result promises, we have to loop over each and // await the result before we can return any result. if (resultPromises) { return (async () => { for (const resultPromise of resultPromises) { const result = await resultPromise; if (typeof result === 'string') { return result; } } return null; })(); } </ins> return null; };

let p = 'scheme:/**/*.md'; assertGlobMatch(p, URI.file('super/duper/long/some/file.md').with({ scheme: 'scheme' }).toString()); }); <ins> test('expression fails when siblings use promises (https://github.com/microsoft/vscode/issues/146294)', async function () { let siblings = ['test.html', 'test.txt', 'test.ts']; let hasSibling = (name: string) => Promise.resolve(siblings.indexOf(name) !== -1); // { "**/*.js": { "when": "$(basename).ts" } } let expression: glob.IExpression = { '**/test.js': { when: '$(basename).js' }, '**/*.js': { when: '$(basename).ts' } }; const parsedExpression = glob.parse(expression); assert.strictEqual('**/*.js', await parsedExpression('test.js', undefined, hasSibling)); }); </ins> });