refactor: split global BUILT_IN_NAMES into per-language provider fields (#523)

* refactor: make isBuiltInOrNoise provider-aware, remove global BUILT_IN_NAMES

Add builtInNames field to LanguageProviderConfig. Rewrite noise-filter.ts
to accept a LanguageProvider and check provider.builtInNames instead of
a global Set. Update all 3 call sites to pass their existing provider.

Built-in entries will be added per-language in subsequent commits.

* refactor(js/ts): add per-language builtInNames to JS/TS providers

* refactor(python): add per-language builtInNames

* refactor(kotlin): add per-language builtInNames

* refactor(c/cpp): add per-language builtInNames

* refactor(csharp): add per-language builtInNames

* refactor(php): add per-language builtInNames

* refactor(swift): add per-language builtInNames

* refactor(rust): add per-language builtInNames

* refactor(ruby): add per-language builtInNames

* refactor(dart): add per-language builtInNames

* test: update noise-filter tests for per-language API, add isolation tests

- Update ingestion-utils.test.ts to pass provider to isBuiltInOrNoise
- Add noise-filter.test.ts with 15 cross-language isolation tests
- Fix Java heritage test: serialize() is now correctly unfiltered for Java
  (was false-positive noise from global PHP serialize entry)

* refactor: remove noise-filter.ts, add provider.isBuiltInName() method

Per review feedback: delete noise-filter.ts entirely and move the check
into LanguageProvider as isBuiltInName(name) method, generated by
defineLanguage() from the builtInNames set.

Call sites now use provider.isBuiltInName(calledName) directly.
This commit is contained in:
Wojciech Guziak 2026-03-26 13:15:29 +01:00 committed by GitHub
parent c6ed25de8b
commit 546128cdcb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 414 additions and 295 deletions

View file

@ -11,7 +11,6 @@ import { getLanguageFromFilename } from './utils/language-detection.js';
import { isVerboseIngestionEnabled } from './utils/verbose.js';
import { yieldToEventLoop } from './utils/event-loop.js';
import { FUNCTION_NODE_TYPES, extractFunctionName, findEnclosingClassId } from './utils/ast-helpers.js';
import { isBuiltInOrNoise } from './utils/noise-filter.js';
import {
countCallArguments,
inferCallForm,
@ -497,7 +496,7 @@ export const processCalls = async (
}
}
if (isBuiltInOrNoise(calledName)) return;
if (provider.isBuiltInName(calledName)) return;
const callNode = captureMap['call'];
const callForm = inferCallForm(callNode, nameNode);

View file

@ -110,6 +110,11 @@ interface LanguageProviderConfig {
* When true, the worker extracts routes via the language's route extraction logic.
* Default: undefined (no route files). */
readonly isRouteFile?: (filePath: string) => boolean;
// ── Noise filtering ────────────────────────────────────────────────
/** Built-in/stdlib names that should be filtered from the call graph for this language.
* Default: undefined (no language-specific filtering). */
readonly builtInNames?: ReadonlySet<string>;
}
/** Runtime type — same as LanguageProviderConfig but with defaults guaranteed present. */
@ -119,6 +124,8 @@ export interface LanguageProvider extends Omit<LanguageProviderConfig,
readonly importSemantics: ImportSemantics;
readonly heritageDefaultEdge: 'EXTENDS' | 'IMPLEMENTS';
readonly mroStrategy: MroStrategy;
/** Check if a name is a built-in/stdlib function that should be filtered from the call graph. */
readonly isBuiltInName: (name: string) => boolean;
}
const DEFAULTS: Pick<LanguageProvider, 'importSemantics' | 'heritageDefaultEdge' | 'mroStrategy'> = {
@ -129,5 +136,10 @@ const DEFAULTS: Pick<LanguageProvider, 'importSemantics' | 'heritageDefaultEdge'
/** Define a language provider — required fields must be supplied, optional fields get sensible defaults. */
export function defineLanguage(config: LanguageProviderConfig): LanguageProvider {
return { ...DEFAULTS, ...config };
const builtIns = config.builtInNames;
return {
...DEFAULTS,
...config,
isBuiltInName: builtIns ? (name: string) => builtIns.has(name) : () => false,
};
}

View file

@ -18,6 +18,28 @@ import { C_QUERIES, CPP_QUERIES } from '../tree-sitter-queries.js';
import { isCppInsideClassOrStruct } from '../utils/ast-helpers.js';
import type { LanguageProvider } from '../language-provider.js';
const C_BUILT_INS: ReadonlySet<string> = new Set([
'printf', 'fprintf', 'sprintf', 'snprintf', 'vprintf', 'vfprintf', 'vsprintf', 'vsnprintf',
'scanf', 'fscanf', 'sscanf',
'malloc', 'calloc', 'realloc', 'free', 'memcpy', 'memmove', 'memset', 'memcmp',
'strlen', 'strcpy', 'strncpy', 'strcat', 'strncat', 'strcmp', 'strncmp', 'strstr', 'strchr', 'strrchr',
'atoi', 'atol', 'atof', 'strtol', 'strtoul', 'strtoll', 'strtoull', 'strtod',
'sizeof', 'offsetof', 'typeof',
'assert', 'abort', 'exit', '_exit',
'fopen', 'fclose', 'fread', 'fwrite', 'fseek', 'ftell', 'rewind', 'fflush', 'fgets', 'fputs',
'likely', 'unlikely', 'BUG', 'BUG_ON', 'WARN', 'WARN_ON', 'WARN_ONCE',
'IS_ERR', 'PTR_ERR', 'ERR_PTR', 'IS_ERR_OR_NULL',
'ARRAY_SIZE', 'container_of', 'list_for_each_entry', 'list_for_each_entry_safe',
'min', 'max', 'clamp', 'abs', 'swap',
'pr_info', 'pr_warn', 'pr_err', 'pr_debug', 'pr_notice', 'pr_crit', 'pr_emerg',
'printk', 'dev_info', 'dev_warn', 'dev_err', 'dev_dbg',
'GFP_KERNEL', 'GFP_ATOMIC',
'spin_lock', 'spin_unlock', 'spin_lock_irqsave', 'spin_unlock_irqrestore',
'mutex_lock', 'mutex_unlock', 'mutex_init',
'kfree', 'kmalloc', 'kzalloc', 'kcalloc', 'krealloc', 'kvmalloc', 'kvfree',
'get', 'put',
]);
/** Label override shared by C and C++: skip function_definition captures inside class/struct
* bodies (they're duplicates of definition.method captures). */
const cppLabelOverride: NonNullable<LanguageProvider['labelOverride']> = (functionNode, defaultLabel) => {
@ -34,6 +56,7 @@ export const cProvider = defineLanguage({
importResolver: resolveCImport,
importSemantics: 'wildcard',
labelOverride: cppLabelOverride,
builtInNames: C_BUILT_INS,
});
export const cppProvider = defineLanguage({
@ -46,4 +69,5 @@ export const cppProvider = defineLanguage({
importSemantics: 'wildcard',
mroStrategy: 'leftmost-base',
labelOverride: cppLabelOverride,
builtInNames: C_BUILT_INS,
});

View file

@ -14,6 +14,27 @@ import { resolveCSharpImport } from '../import-resolvers/csharp.js';
import { extractCSharpNamedBindings } from '../named-bindings/csharp.js';
import { CSHARP_QUERIES } from '../tree-sitter-queries.js';
const BUILT_INS: ReadonlySet<string> = new Set([
'Console', 'WriteLine', 'ReadLine', 'Write',
'Task', 'Run', 'Wait', 'WhenAll', 'WhenAny', 'FromResult', 'Delay', 'ContinueWith',
'ConfigureAwait', 'GetAwaiter', 'GetResult',
'ToString', 'GetType', 'Equals', 'GetHashCode', 'ReferenceEquals',
'Add', 'Remove', 'Contains', 'Clear', 'Count', 'Any', 'All',
'Where', 'Select', 'SelectMany', 'OrderBy', 'OrderByDescending', 'GroupBy',
'First', 'FirstOrDefault', 'Single', 'SingleOrDefault', 'Last', 'LastOrDefault',
'ToList', 'ToArray', 'ToDictionary', 'AsEnumerable', 'AsQueryable',
'Aggregate', 'Sum', 'Average', 'Min', 'Max', 'Distinct', 'Skip', 'Take',
'String', 'Format', 'IsNullOrEmpty', 'IsNullOrWhiteSpace', 'Concat', 'Join',
'Trim', 'TrimStart', 'TrimEnd', 'Split', 'Replace', 'StartsWith', 'EndsWith',
'Convert', 'ToInt32', 'ToDouble', 'ToBoolean', 'ToByte',
'Math', 'Abs', 'Ceiling', 'Floor', 'Round', 'Pow', 'Sqrt',
'Dispose', 'Close',
'TryParse', 'Parse',
'AddRange', 'RemoveAt', 'RemoveAll', 'FindAll', 'Exists', 'TrueForAll',
'ContainsKey', 'TryGetValue', 'AddOrUpdate',
'Throw', 'ThrowIfNull',
]);
export const csharpProvider = defineLanguage({
id: SupportedLanguages.CSharp,
extensions: ['.cs'],
@ -24,4 +45,5 @@ export const csharpProvider = defineLanguage({
namedBindingExtractor: extractCSharpNamedBindings,
interfaceNamePattern: /^I[A-Z]/,
mroStrategy: 'implements-split',
builtInNames: BUILT_INS,
});

View file

@ -14,6 +14,15 @@ import { dartExportChecker } from '../export-detection.js';
import { resolveDartImport } from '../import-resolvers/dart.js';
import { DART_QUERIES } from '../tree-sitter-queries.js';
const BUILT_INS: ReadonlySet<string> = new Set([
'setState', 'mounted', 'debugPrint',
'runApp', 'showDialog', 'showModalBottomSheet',
'Navigator', 'push', 'pushNamed', 'pushReplacement', 'pop', 'maybePop',
'ScaffoldMessenger', 'showSnackBar',
'deactivate', 'reassemble', 'debugDumpApp', 'debugDumpRenderTree',
'then', 'catchError', 'whenComplete', 'listen',
]);
export const dartProvider = defineLanguage({
id: SupportedLanguages.Dart,
extensions: ['.dart'],
@ -22,4 +31,5 @@ export const dartProvider = defineLanguage({
exportChecker: dartExportChecker,
importResolver: resolveDartImport,
importSemantics: 'wildcard',
builtInNames: BUILT_INS,
});

View file

@ -17,6 +17,21 @@ import { appendKotlinWildcard } from '../import-resolvers/jvm.js';
import { KOTLIN_QUERIES } from '../tree-sitter-queries.js';
import { isKotlinClassMethod } from '../utils/ast-helpers.js';
const BUILT_INS: ReadonlySet<string> = new Set([
'println', 'print', 'readLine', 'require', 'requireNotNull', 'check', 'assert', 'lazy', 'error',
'listOf', 'mapOf', 'setOf', 'mutableListOf', 'mutableMapOf', 'mutableSetOf',
'arrayOf', 'sequenceOf', 'also', 'apply', 'run', 'with', 'takeIf', 'takeUnless',
'TODO', 'buildString', 'buildList', 'buildMap', 'buildSet',
'repeat', 'synchronized',
'launch', 'async', 'runBlocking', 'withContext', 'coroutineScope',
'supervisorScope', 'delay',
'flow', 'flowOf', 'collect', 'emit', 'onEach', 'catch',
'buffer', 'conflate', 'distinctUntilChanged',
'flatMapLatest', 'flatMapMerge', 'combine',
'stateIn', 'shareIn', 'launchIn',
'to', 'until', 'downTo', 'step',
]);
export const kotlinProvider = defineLanguage({
id: SupportedLanguages.Kotlin,
extensions: ['.kt', '.kts'],
@ -27,6 +42,7 @@ export const kotlinProvider = defineLanguage({
namedBindingExtractor: extractKotlinNamedBindings,
importPathPreprocessor: appendKotlinWildcard,
mroStrategy: 'implements-split',
builtInNames: BUILT_INS,
labelOverride: (functionNode, defaultLabel) => {
if (defaultLabel !== 'Function') return defaultLabel;
if (isKotlinClassMethod(functionNode)) return 'Method';

View file

@ -16,6 +16,25 @@ import { PHP_QUERIES } from '../tree-sitter-queries.js';
import { findDescendant, extractStringContent } from '../utils/ast-helpers.js';
import type { NodeLabel } from '../../graph/types.js';
const BUILT_INS: ReadonlySet<string> = new Set([
'echo', 'isset', 'empty', 'unset', 'list', 'array', 'compact', 'extract',
'count', 'strlen', 'strpos', 'strrpos', 'substr', 'strtolower', 'strtoupper', 'trim',
'ltrim', 'rtrim', 'str_replace', 'str_contains', 'str_starts_with', 'str_ends_with',
'sprintf', 'vsprintf', 'printf', 'number_format',
'array_map', 'array_filter', 'array_reduce', 'array_push', 'array_pop', 'array_shift',
'array_unshift', 'array_slice', 'array_splice', 'array_merge', 'array_keys', 'array_values',
'array_key_exists', 'in_array', 'array_search', 'array_unique', 'usort', 'rsort',
'json_encode', 'json_decode', 'serialize', 'unserialize',
'intval', 'floatval', 'strval', 'boolval', 'is_null', 'is_string', 'is_int', 'is_array',
'is_object', 'is_numeric', 'is_bool', 'is_float',
'var_dump', 'print_r', 'var_export',
'date', 'time', 'strtotime', 'mktime', 'microtime',
'file_exists', 'file_get_contents', 'file_put_contents', 'is_file', 'is_dir',
'preg_match', 'preg_match_all', 'preg_replace', 'preg_split',
'header', 'session_start', 'session_destroy', 'ob_start', 'ob_end_clean', 'ob_get_clean',
'dd', 'dump',
]);
/** Eloquent model properties whose array values are worth indexing. */
const ELOQUENT_ARRAY_PROPS = new Set(['fillable', 'casts', 'hidden', 'guarded', 'with', 'appends']);
@ -130,4 +149,5 @@ export const phpProvider = defineLanguage({
namedBindingExtractor: extractPhpNamedBindings,
descriptionExtractor: phpDescriptionExtractor,
isRouteFile: isPhpRouteFile,
builtInNames: BUILT_INS,
});

View file

@ -18,6 +18,13 @@ import { resolvePythonImport } from '../import-resolvers/python.js';
import { extractPythonNamedBindings } from '../named-bindings/python.js';
import { PYTHON_QUERIES } from '../tree-sitter-queries.js';
const BUILT_INS: ReadonlySet<string> = new Set([
'print', 'len', 'range', 'str', 'int', 'float', 'list', 'dict', 'set', 'tuple',
'append', 'extend', 'update',
'type', 'isinstance', 'issubclass', 'getattr', 'setattr', 'hasattr',
'enumerate', 'zip', 'sorted', 'reversed', 'min', 'max', 'sum', 'abs',
]);
export const pythonProvider = defineLanguage({
id: SupportedLanguages.Python,
extensions: ['.py'],
@ -28,4 +35,5 @@ export const pythonProvider = defineLanguage({
namedBindingExtractor: extractPythonNamedBindings,
importSemantics: 'namespace',
mroStrategy: 'c3',
builtInNames: BUILT_INS,
});

View file

@ -15,6 +15,22 @@ import { rubyExportChecker } from '../export-detection.js';
import { resolveRubyImport } from '../import-resolvers/ruby.js';
import { RUBY_QUERIES } from '../tree-sitter-queries.js';
const BUILT_INS: ReadonlySet<string> = new Set([
'puts', 'p', 'pp', 'raise', 'fail',
'require', 'require_relative', 'load', 'autoload',
'include', 'extend', 'prepend',
'attr_accessor', 'attr_reader', 'attr_writer',
'public', 'private', 'protected', 'module_function',
'lambda', 'proc', 'block_given?',
'nil?', 'is_a?', 'kind_of?', 'instance_of?', 'respond_to?',
'freeze', 'frozen?', 'dup', 'tap', 'yield_self',
'each', 'select', 'reject', 'detect', 'collect',
'inject', 'flat_map', 'each_with_object', 'each_with_index',
'any?', 'all?', 'none?', 'count', 'first', 'last',
'sort_by', 'min_by', 'max_by',
'group_by', 'partition', 'compact', 'flatten', 'uniq',
]);
export const rubyProvider = defineLanguage({
id: SupportedLanguages.Ruby,
extensions: ['.rb', '.rake', '.gemspec'],
@ -24,4 +40,5 @@ export const rubyProvider = defineLanguage({
importResolver: resolveRubyImport,
callRouter: routeRubyCall,
importSemantics: 'wildcard',
builtInNames: BUILT_INS,
});

View file

@ -18,6 +18,19 @@ import { resolveRustImport } from '../import-resolvers/rust.js';
import { extractRustNamedBindings } from '../named-bindings/rust.js';
import { RUST_QUERIES } from '../tree-sitter-queries.js';
const BUILT_INS: ReadonlySet<string> = new Set([
'unwrap', 'expect', 'unwrap_or', 'unwrap_or_else', 'unwrap_or_default',
'ok', 'err', 'is_ok', 'is_err', 'map', 'map_err', 'and_then', 'or_else',
'clone', 'to_string', 'to_owned', 'into', 'from', 'as_ref', 'as_mut',
'iter', 'into_iter', 'collect', 'filter', 'fold', 'for_each',
'len', 'is_empty', 'push', 'pop', 'insert', 'remove', 'contains',
'format', 'write', 'writeln', 'panic', 'unreachable', 'todo', 'unimplemented',
'vec', 'println', 'eprintln', 'dbg',
'lock', 'read', 'try_lock',
'spawn', 'join', 'sleep',
'Some', 'None', 'Ok', 'Err',
]);
export const rustProvider = defineLanguage({
id: SupportedLanguages.Rust,
extensions: ['.rs'],
@ -27,4 +40,5 @@ export const rustProvider = defineLanguage({
importResolver: resolveRustImport,
namedBindingExtractor: extractRustNamedBindings,
mroStrategy: 'qualified-syntax',
builtInNames: BUILT_INS,
});

View file

@ -101,6 +101,34 @@ function wireSwiftImplicitImports(
}
}
const BUILT_INS: ReadonlySet<string> = new Set([
'print', 'debugPrint', 'dump', 'fatalError', 'precondition', 'preconditionFailure',
'assert', 'assertionFailure', 'NSLog',
'abs', 'min', 'max', 'zip', 'stride', 'sequence', 'repeatElement',
'swap', 'withUnsafePointer', 'withUnsafeMutablePointer', 'withUnsafeBytes',
'autoreleasepool', 'unsafeBitCast', 'unsafeDowncast', 'numericCast',
'type', 'MemoryLayout',
'map', 'flatMap', 'compactMap', 'filter', 'reduce', 'forEach', 'contains',
'first', 'last', 'prefix', 'suffix', 'dropFirst', 'dropLast',
'sorted', 'reversed', 'enumerated', 'joined', 'split',
'append', 'insert', 'remove', 'removeAll', 'removeFirst', 'removeLast',
'isEmpty', 'count', 'index', 'startIndex', 'endIndex',
'addSubview', 'removeFromSuperview', 'layoutSubviews', 'setNeedsLayout',
'layoutIfNeeded', 'setNeedsDisplay', 'invalidateIntrinsicContentSize',
'addTarget', 'removeTarget', 'addGestureRecognizer',
'addConstraint', 'addConstraints', 'removeConstraint', 'removeConstraints',
'NSLocalizedString', 'Bundle',
'reloadData', 'reloadSections', 'reloadRows', 'performBatchUpdates',
'register', 'dequeueReusableCell', 'dequeueReusableSupplementaryView',
'beginUpdates', 'endUpdates', 'insertRows', 'deleteRows', 'insertSections', 'deleteSections',
'present', 'dismiss', 'pushViewController', 'popViewController', 'popToRootViewController',
'performSegue', 'prepare',
'DispatchQueue', 'async', 'sync', 'asyncAfter',
'Task', 'withCheckedContinuation', 'withCheckedThrowingContinuation',
'sink', 'store', 'assign', 'receive', 'subscribe',
'addObserver', 'removeObserver', 'post', 'NotificationCenter',
]);
export const swiftProvider = defineLanguage({
id: SupportedLanguages.Swift,
extensions: ['.swift'],
@ -111,4 +139,5 @@ export const swiftProvider = defineLanguage({
importSemantics: 'wildcard',
heritageDefaultEdge: 'IMPLEMENTS',
implicitImportWirer: wireSwiftImplicitImports,
builtInNames: BUILT_INS,
});

View file

@ -15,6 +15,27 @@ import { resolveTypescriptImport, resolveJavascriptImport } from '../import-reso
import { extractTsNamedBindings } from '../named-bindings/typescript.js';
import { TYPESCRIPT_QUERIES, JAVASCRIPT_QUERIES } from '../tree-sitter-queries.js';
const BUILT_INS: ReadonlySet<string> = new Set([
'console', 'log', 'warn', 'error', 'info', 'debug',
'setTimeout', 'setInterval', 'clearTimeout', 'clearInterval',
'parseInt', 'parseFloat', 'isNaN', 'isFinite',
'encodeURI', 'decodeURI', 'encodeURIComponent', 'decodeURIComponent',
'JSON', 'parse', 'stringify',
'Object', 'Array', 'String', 'Number', 'Boolean', 'Symbol', 'BigInt',
'Map', 'Set', 'WeakMap', 'WeakSet',
'Promise', 'resolve', 'reject', 'then', 'catch', 'finally',
'Math', 'Date', 'RegExp', 'Error',
'require', 'import', 'export', 'fetch', 'Response', 'Request',
'useState', 'useEffect', 'useCallback', 'useMemo', 'useRef', 'useContext',
'useReducer', 'useLayoutEffect', 'useImperativeHandle', 'useDebugValue',
'createElement', 'createContext', 'createRef', 'forwardRef', 'memo', 'lazy',
'map', 'filter', 'reduce', 'forEach', 'find', 'findIndex', 'some', 'every',
'includes', 'indexOf', 'slice', 'splice', 'concat', 'join', 'split',
'push', 'pop', 'shift', 'unshift', 'sort', 'reverse',
'keys', 'values', 'entries', 'assign', 'freeze', 'seal',
'hasOwnProperty', 'toString', 'valueOf',
]);
export const typescriptProvider = defineLanguage({
id: SupportedLanguages.TypeScript,
extensions: ['.ts', '.tsx'],
@ -23,6 +44,7 @@ export const typescriptProvider = defineLanguage({
exportChecker: tsExportChecker,
importResolver: resolveTypescriptImport,
namedBindingExtractor: extractTsNamedBindings,
builtInNames: BUILT_INS,
});
export const javascriptProvider = defineLanguage({
@ -33,4 +55,5 @@ export const javascriptProvider = defineLanguage({
exportChecker: tsExportChecker,
importResolver: resolveJavascriptImport,
namedBindingExtractor: extractTsNamedBindings,
builtInNames: BUILT_INS,
});

View file

@ -808,7 +808,7 @@ export const RUBY_QUERIES = `
; NOTE: This may over-capture variable reads as calls (e.g. 'result' at
; statement level). Ruby's grammar makes bare identifiers ambiguous they
; could be local variables or zero-arity method calls. Post-processing via
; isBuiltInOrNoise and symbol resolution filtering suppresses most false
; provider.isBuiltInName and symbol resolution filtering suppresses most false
; positives, but a variable name that coincidentally matches a method name
; elsewhere may produce a false CALLS edge.
(body_statement

View file

@ -1,6 +1,5 @@
import { type SyntaxNode, FUNCTION_NODE_TYPES, extractFunctionName, CLASS_CONTAINER_TYPES } from './utils/ast-helpers.js';
import { CALL_EXPRESSION_TYPES } from './utils/call-analysis.js';
import { isBuiltInOrNoise } from './utils/noise-filter.js';
import { SupportedLanguages } from '../../config/supported-languages.js';
import { TYPED_PARAMETER_TYPES } from './type-extractors/shared.js';
import { getProvider } from './languages/index.js';
@ -729,7 +728,7 @@ export const buildTypeEnv = (
lookupReturnType(callee: string): string | undefined {
// SymbolTable is authoritative when it has an unambiguous match
if (symbolTable) {
if (isBuiltInOrNoise(callee)) return undefined;
if (provider.isBuiltInName(callee)) return undefined;
const callables = symbolTable.lookupFuzzyCallable(callee);
if (callables.length === 1) {
const rawReturn = callables[0].returnType;
@ -743,7 +742,7 @@ export const buildTypeEnv = (
},
lookupRawReturnType(callee: string): string | undefined {
if (symbolTable) {
if (isBuiltInOrNoise(callee)) return undefined;
if (provider.isBuiltInName(callee)) return undefined;
const callables = symbolTable.lookupFuzzyCallable(callee);
if (callables.length === 1) return callables[0].returnType;
// Ambiguous (2+) → return undefined (conservative, no cross-file fallback)

View file

@ -1,175 +0,0 @@
/**
* Built-in name filtering identifies standard library functions and common noise
* that should not be tracked as call targets in the knowledge graph.
*
* Covers: JS/TS, Python, Kotlin, C/C++, C#, PHP, Swift, Rust, Ruby, Dart/Flutter standard libraries.
*/
export const BUILT_IN_NAMES = new Set([
// JavaScript/TypeScript
'console', 'log', 'warn', 'error', 'info', 'debug',
'setTimeout', 'setInterval', 'clearTimeout', 'clearInterval',
'parseInt', 'parseFloat', 'isNaN', 'isFinite',
'encodeURI', 'decodeURI', 'encodeURIComponent', 'decodeURIComponent',
'JSON', 'parse', 'stringify',
'Object', 'Array', 'String', 'Number', 'Boolean', 'Symbol', 'BigInt',
'Map', 'Set', 'WeakMap', 'WeakSet',
'Promise', 'resolve', 'reject', 'then', 'catch', 'finally',
'Math', 'Date', 'RegExp', 'Error',
'require', 'import', 'export', 'fetch', 'Response', 'Request',
'useState', 'useEffect', 'useCallback', 'useMemo', 'useRef', 'useContext',
'useReducer', 'useLayoutEffect', 'useImperativeHandle', 'useDebugValue',
'createElement', 'createContext', 'createRef', 'forwardRef', 'memo', 'lazy',
'map', 'filter', 'reduce', 'forEach', 'find', 'findIndex', 'some', 'every',
'includes', 'indexOf', 'slice', 'splice', 'concat', 'join', 'split',
'push', 'pop', 'shift', 'unshift', 'sort', 'reverse',
'keys', 'values', 'entries', 'assign', 'freeze', 'seal',
'hasOwnProperty', 'toString', 'valueOf',
// Python
'print', 'len', 'range', 'str', 'int', 'float', 'list', 'dict', 'set', 'tuple',
'append', 'extend', 'update',
// NOTE: 'open', 'read', 'write', 'close' removed — these are real C POSIX syscalls
'type', 'isinstance', 'issubclass', 'getattr', 'setattr', 'hasattr',
'enumerate', 'zip', 'sorted', 'reversed', 'min', 'max', 'sum', 'abs',
// Kotlin stdlib
'println', 'print', 'readLine', 'require', 'requireNotNull', 'check', 'assert', 'lazy', 'error',
'listOf', 'mapOf', 'setOf', 'mutableListOf', 'mutableMapOf', 'mutableSetOf',
'arrayOf', 'sequenceOf', 'also', 'apply', 'run', 'with', 'takeIf', 'takeUnless',
'TODO', 'buildString', 'buildList', 'buildMap', 'buildSet',
'repeat', 'synchronized',
// Kotlin coroutine builders & scope functions
'launch', 'async', 'runBlocking', 'withContext', 'coroutineScope',
'supervisorScope', 'delay',
// Kotlin Flow operators
'flow', 'flowOf', 'collect', 'emit', 'onEach', 'catch',
'buffer', 'conflate', 'distinctUntilChanged',
'flatMapLatest', 'flatMapMerge', 'combine',
'stateIn', 'shareIn', 'launchIn',
// Kotlin infix stdlib functions
'to', 'until', 'downTo', 'step',
// C/C++ standard library
'printf', 'fprintf', 'sprintf', 'snprintf', 'vprintf', 'vfprintf', 'vsprintf', 'vsnprintf',
'scanf', 'fscanf', 'sscanf',
'malloc', 'calloc', 'realloc', 'free', 'memcpy', 'memmove', 'memset', 'memcmp',
'strlen', 'strcpy', 'strncpy', 'strcat', 'strncat', 'strcmp', 'strncmp', 'strstr', 'strchr', 'strrchr',
'atoi', 'atol', 'atof', 'strtol', 'strtoul', 'strtoll', 'strtoull', 'strtod',
'sizeof', 'offsetof', 'typeof',
'assert', 'abort', 'exit', '_exit',
'fopen', 'fclose', 'fread', 'fwrite', 'fseek', 'ftell', 'rewind', 'fflush', 'fgets', 'fputs',
// Linux kernel common macros/helpers (not real call targets)
'likely', 'unlikely', 'BUG', 'BUG_ON', 'WARN', 'WARN_ON', 'WARN_ONCE',
'IS_ERR', 'PTR_ERR', 'ERR_PTR', 'IS_ERR_OR_NULL',
'ARRAY_SIZE', 'container_of', 'list_for_each_entry', 'list_for_each_entry_safe',
'min', 'max', 'clamp', 'abs', 'swap',
'pr_info', 'pr_warn', 'pr_err', 'pr_debug', 'pr_notice', 'pr_crit', 'pr_emerg',
'printk', 'dev_info', 'dev_warn', 'dev_err', 'dev_dbg',
'GFP_KERNEL', 'GFP_ATOMIC',
'spin_lock', 'spin_unlock', 'spin_lock_irqsave', 'spin_unlock_irqrestore',
'mutex_lock', 'mutex_unlock', 'mutex_init',
'kfree', 'kmalloc', 'kzalloc', 'kcalloc', 'krealloc', 'kvmalloc', 'kvfree',
'get', 'put',
// C# / .NET built-ins
'Console', 'WriteLine', 'ReadLine', 'Write',
'Task', 'Run', 'Wait', 'WhenAll', 'WhenAny', 'FromResult', 'Delay', 'ContinueWith',
'ConfigureAwait', 'GetAwaiter', 'GetResult',
'ToString', 'GetType', 'Equals', 'GetHashCode', 'ReferenceEquals',
'Add', 'Remove', 'Contains', 'Clear', 'Count', 'Any', 'All',
'Where', 'Select', 'SelectMany', 'OrderBy', 'OrderByDescending', 'GroupBy',
'First', 'FirstOrDefault', 'Single', 'SingleOrDefault', 'Last', 'LastOrDefault',
'ToList', 'ToArray', 'ToDictionary', 'AsEnumerable', 'AsQueryable',
'Aggregate', 'Sum', 'Average', 'Min', 'Max', 'Distinct', 'Skip', 'Take',
'String', 'Format', 'IsNullOrEmpty', 'IsNullOrWhiteSpace', 'Concat', 'Join',
'Trim', 'TrimStart', 'TrimEnd', 'Split', 'Replace', 'StartsWith', 'EndsWith',
'Convert', 'ToInt32', 'ToDouble', 'ToBoolean', 'ToByte',
'Math', 'Abs', 'Ceiling', 'Floor', 'Round', 'Pow', 'Sqrt',
'Dispose', 'Close',
'TryParse', 'Parse',
'AddRange', 'RemoveAt', 'RemoveAll', 'FindAll', 'Exists', 'TrueForAll',
'ContainsKey', 'TryGetValue', 'AddOrUpdate',
'Throw', 'ThrowIfNull',
// PHP built-ins
'echo', 'isset', 'empty', 'unset', 'list', 'array', 'compact', 'extract',
'count', 'strlen', 'strpos', 'strrpos', 'substr', 'strtolower', 'strtoupper', 'trim',
'ltrim', 'rtrim', 'str_replace', 'str_contains', 'str_starts_with', 'str_ends_with',
'sprintf', 'vsprintf', 'printf', 'number_format',
'array_map', 'array_filter', 'array_reduce', 'array_push', 'array_pop', 'array_shift',
'array_unshift', 'array_slice', 'array_splice', 'array_merge', 'array_keys', 'array_values',
'array_key_exists', 'in_array', 'array_search', 'array_unique', 'usort', 'rsort',
'json_encode', 'json_decode', 'serialize', 'unserialize',
'intval', 'floatval', 'strval', 'boolval', 'is_null', 'is_string', 'is_int', 'is_array',
'is_object', 'is_numeric', 'is_bool', 'is_float',
'var_dump', 'print_r', 'var_export',
'date', 'time', 'strtotime', 'mktime', 'microtime',
'file_exists', 'file_get_contents', 'file_put_contents', 'is_file', 'is_dir',
'preg_match', 'preg_match_all', 'preg_replace', 'preg_split',
'header', 'session_start', 'session_destroy', 'ob_start', 'ob_end_clean', 'ob_get_clean',
'dd', 'dump',
// Swift/iOS built-ins and standard library
'print', 'debugPrint', 'dump', 'fatalError', 'precondition', 'preconditionFailure',
'assert', 'assertionFailure', 'NSLog',
'abs', 'min', 'max', 'zip', 'stride', 'sequence', 'repeatElement',
'swap', 'withUnsafePointer', 'withUnsafeMutablePointer', 'withUnsafeBytes',
'autoreleasepool', 'unsafeBitCast', 'unsafeDowncast', 'numericCast',
'type', 'MemoryLayout',
// Swift collection/string methods (common noise)
'map', 'flatMap', 'compactMap', 'filter', 'reduce', 'forEach', 'contains',
'first', 'last', 'prefix', 'suffix', 'dropFirst', 'dropLast',
'sorted', 'reversed', 'enumerated', 'joined', 'split',
'append', 'insert', 'remove', 'removeAll', 'removeFirst', 'removeLast',
'isEmpty', 'count', 'index', 'startIndex', 'endIndex',
// UIKit/Foundation common methods (noise in call graph)
'addSubview', 'removeFromSuperview', 'layoutSubviews', 'setNeedsLayout',
'layoutIfNeeded', 'setNeedsDisplay', 'invalidateIntrinsicContentSize',
'addTarget', 'removeTarget', 'addGestureRecognizer',
'addConstraint', 'addConstraints', 'removeConstraint', 'removeConstraints',
'NSLocalizedString', 'Bundle',
'reloadData', 'reloadSections', 'reloadRows', 'performBatchUpdates',
'register', 'dequeueReusableCell', 'dequeueReusableSupplementaryView',
'beginUpdates', 'endUpdates', 'insertRows', 'deleteRows', 'insertSections', 'deleteSections',
'present', 'dismiss', 'pushViewController', 'popViewController', 'popToRootViewController',
'performSegue', 'prepare',
// GCD / async
'DispatchQueue', 'async', 'sync', 'asyncAfter',
'Task', 'withCheckedContinuation', 'withCheckedThrowingContinuation',
// Combine
'sink', 'store', 'assign', 'receive', 'subscribe',
// Notification / KVO
'addObserver', 'removeObserver', 'post', 'NotificationCenter',
// Rust standard library (common noise in call graphs)
'unwrap', 'expect', 'unwrap_or', 'unwrap_or_else', 'unwrap_or_default',
'ok', 'err', 'is_ok', 'is_err', 'map', 'map_err', 'and_then', 'or_else',
'clone', 'to_string', 'to_owned', 'into', 'from', 'as_ref', 'as_mut',
'iter', 'into_iter', 'collect', 'map', 'filter', 'fold', 'for_each',
'len', 'is_empty', 'push', 'pop', 'insert', 'remove', 'contains',
'format', 'write', 'writeln', 'panic', 'unreachable', 'todo', 'unimplemented',
'vec', 'println', 'eprintln', 'dbg',
'lock', 'read', 'write', 'try_lock',
'spawn', 'join', 'sleep',
'Some', 'None', 'Ok', 'Err',
// Ruby built-ins and Kernel methods
'puts', 'p', 'pp', 'raise', 'fail',
'require', 'require_relative', 'load', 'autoload',
'include', 'extend', 'prepend',
'attr_accessor', 'attr_reader', 'attr_writer',
'public', 'private', 'protected', 'module_function',
'lambda', 'proc', 'block_given?',
'nil?', 'is_a?', 'kind_of?', 'instance_of?', 'respond_to?',
'freeze', 'frozen?', 'dup', 'tap', 'yield_self',
// Dart / Flutter
'setState', 'mounted', 'debugPrint',
'runApp', 'showDialog', 'showModalBottomSheet',
'Navigator', 'push', 'pushNamed', 'pushReplacement', 'pop', 'maybePop',
'ScaffoldMessenger', 'showSnackBar',
'deactivate', 'reassemble', 'debugDumpApp', 'debugDumpRenderTree',
// Dart async
'then', 'catchError', 'whenComplete', 'listen',
// Ruby enumerables
'each', 'select', 'reject', 'detect', 'collect',
'inject', 'flat_map', 'each_with_object', 'each_with_index',
'any?', 'all?', 'none?', 'count', 'first', 'last',
'sort_by', 'min_by', 'max_by',
'group_by', 'partition', 'compact', 'flatten', 'uniq',
]);
/** Check if a name is a built-in function or common noise that should be filtered out */
export const isBuiltInOrNoise = (name: string): boolean => BUILT_IN_NAMES.has(name);

View file

@ -29,7 +29,6 @@ try { Dart = _require('tree-sitter-dart'); } catch {}
let Kotlin: any = null;
try { Kotlin = _require('tree-sitter-kotlin'); } catch {}
import { getLanguageFromFilename } from '../utils/language-detection.js';
import { isBuiltInOrNoise } from '../utils/noise-filter.js';
import {
FUNCTION_NODE_TYPES,
extractFunctionName,
@ -1181,7 +1180,7 @@ const processFileGroup = (
// kind === 'call' — fall through to normal call processing below
}
if (!isBuiltInOrNoise(calledName)) {
if (!provider.isBuiltInName(calledName)) {
const callNode = captureMap['call'];
const sourceId = findEnclosingFunctionId(callNode, file.path, provider)
|| generateId('File', file.path);

View file

@ -60,11 +60,12 @@ describe('Java heritage resolution', () => {
expect(extends_.some(e => e.target === 'Validatable')).toBe(false);
});
it('emits exactly 2 CALLS edges', () => {
it('emits exactly 3 CALLS edges', () => {
const calls = getRelationships(result, 'CALLS');
expect(calls.length).toBe(2);
expect(calls.length).toBe(3);
expect(edgeSet(calls)).toEqual([
'processUser → save',
'processUser → serialize',
'processUser → validate',
]);
});

View file

@ -1,9 +1,9 @@
import { describe, it, expect } from 'vitest';
import { getLanguageFromFilename } from '../../src/core/ingestion/utils/language-detection.js';
import { isBuiltInOrNoise } from '../../src/core/ingestion/utils/noise-filter.js';
import { getProvider } from '../../src/core/ingestion/languages/index.js';
import { SupportedLanguages } from '../../src/config/supported-languages.js';
import { extractFunctionName } from '../../src/core/ingestion/utils/ast-helpers.js';
import { getTreeSitterBufferSize, TREE_SITTER_BUFFER_SIZE, TREE_SITTER_MAX_BUFFER } from '../../src/core/ingestion/constants.js';
import { SupportedLanguages } from '../../src/config/supported-languages.js';
import Parser from 'tree-sitter';
import C from 'tree-sitter-c';
import CPP from 'tree-sitter-cpp';
@ -140,203 +140,212 @@ describe('getLanguageFromFilename', () => {
});
describe('isBuiltInOrNoise', () => {
const js = getProvider(SupportedLanguages.JavaScript);
const py = getProvider(SupportedLanguages.Python);
const php = getProvider(SupportedLanguages.PHP);
const c = getProvider(SupportedLanguages.C);
const kt = getProvider(SupportedLanguages.Kotlin);
const swift = getProvider(SupportedLanguages.Swift);
const rust = getProvider(SupportedLanguages.Rust);
const cs = getProvider(SupportedLanguages.CSharp);
describe('JavaScript/TypeScript', () => {
it('filters console methods', () => {
expect(isBuiltInOrNoise('console')).toBe(true);
expect(isBuiltInOrNoise('log')).toBe(true);
expect(isBuiltInOrNoise('warn')).toBe(true);
expect(js.isBuiltInName('console')).toBe(true);
expect(js.isBuiltInName('log')).toBe(true);
expect(js.isBuiltInName('warn')).toBe(true);
});
it('filters React hooks', () => {
expect(isBuiltInOrNoise('useState')).toBe(true);
expect(isBuiltInOrNoise('useEffect')).toBe(true);
expect(isBuiltInOrNoise('useCallback')).toBe(true);
expect(js.isBuiltInName('useState')).toBe(true);
expect(js.isBuiltInName('useEffect')).toBe(true);
expect(js.isBuiltInName('useCallback')).toBe(true);
});
it('filters array methods', () => {
expect(isBuiltInOrNoise('map')).toBe(true);
expect(isBuiltInOrNoise('filter')).toBe(true);
expect(isBuiltInOrNoise('reduce')).toBe(true);
expect(js.isBuiltInName('map')).toBe(true);
expect(js.isBuiltInName('filter')).toBe(true);
expect(js.isBuiltInName('reduce')).toBe(true);
});
});
describe('Python', () => {
it('filters built-in functions', () => {
expect(isBuiltInOrNoise('print')).toBe(true);
expect(isBuiltInOrNoise('len')).toBe(true);
expect(isBuiltInOrNoise('range')).toBe(true);
expect(py.isBuiltInName('print')).toBe(true);
expect(py.isBuiltInName('len')).toBe(true);
expect(py.isBuiltInName('range')).toBe(true);
});
});
describe('PHP', () => {
it('filters PHP built-in functions', () => {
expect(isBuiltInOrNoise('echo')).toBe(true);
expect(isBuiltInOrNoise('isset')).toBe(true);
expect(isBuiltInOrNoise('date')).toBe(true);
expect(isBuiltInOrNoise('json_encode')).toBe(true);
expect(isBuiltInOrNoise('array_map')).toBe(true);
expect(php.isBuiltInName('echo')).toBe(true);
expect(php.isBuiltInName('isset')).toBe(true);
expect(php.isBuiltInName('date')).toBe(true);
expect(php.isBuiltInName('json_encode')).toBe(true);
expect(php.isBuiltInName('array_map')).toBe(true);
});
it('filters PHP string functions', () => {
expect(isBuiltInOrNoise('strlen')).toBe(true);
expect(isBuiltInOrNoise('substr')).toBe(true);
expect(isBuiltInOrNoise('str_replace')).toBe(true);
expect(php.isBuiltInName('strlen')).toBe(true);
expect(php.isBuiltInName('substr')).toBe(true);
expect(php.isBuiltInName('str_replace')).toBe(true);
});
});
describe('C/C++', () => {
it('filters standard library functions', () => {
expect(isBuiltInOrNoise('printf')).toBe(true);
expect(isBuiltInOrNoise('malloc')).toBe(true);
expect(isBuiltInOrNoise('free')).toBe(true);
expect(c.isBuiltInName('printf')).toBe(true);
expect(c.isBuiltInName('malloc')).toBe(true);
expect(c.isBuiltInName('free')).toBe(true);
});
it('filters Linux kernel macros', () => {
expect(isBuiltInOrNoise('container_of')).toBe(true);
expect(isBuiltInOrNoise('ARRAY_SIZE')).toBe(true);
expect(isBuiltInOrNoise('pr_info')).toBe(true);
expect(c.isBuiltInName('container_of')).toBe(true);
expect(c.isBuiltInName('ARRAY_SIZE')).toBe(true);
expect(c.isBuiltInName('pr_info')).toBe(true);
});
});
describe('Kotlin', () => {
it('filters stdlib functions', () => {
expect(isBuiltInOrNoise('println')).toBe(true);
expect(isBuiltInOrNoise('listOf')).toBe(true);
expect(isBuiltInOrNoise('TODO')).toBe(true);
expect(kt.isBuiltInName('println')).toBe(true);
expect(kt.isBuiltInName('listOf')).toBe(true);
expect(kt.isBuiltInName('TODO')).toBe(true);
});
it('filters coroutine functions', () => {
expect(isBuiltInOrNoise('launch')).toBe(true);
expect(isBuiltInOrNoise('async')).toBe(true);
expect(kt.isBuiltInName('launch')).toBe(true);
expect(kt.isBuiltInName('async')).toBe(true);
});
});
describe('Swift', () => {
it('filters built-in functions', () => {
expect(isBuiltInOrNoise('print')).toBe(true);
expect(isBuiltInOrNoise('fatalError')).toBe(true);
expect(swift.isBuiltInName('print')).toBe(true);
expect(swift.isBuiltInName('fatalError')).toBe(true);
});
it('filters UIKit methods', () => {
expect(isBuiltInOrNoise('addSubview')).toBe(true);
expect(isBuiltInOrNoise('reloadData')).toBe(true);
expect(swift.isBuiltInName('addSubview')).toBe(true);
expect(swift.isBuiltInName('reloadData')).toBe(true);
});
});
describe('Rust', () => {
it('filters Result/Option methods', () => {
expect(isBuiltInOrNoise('unwrap')).toBe(true);
expect(isBuiltInOrNoise('expect')).toBe(true);
expect(isBuiltInOrNoise('unwrap_or')).toBe(true);
expect(isBuiltInOrNoise('unwrap_or_else')).toBe(true);
expect(isBuiltInOrNoise('unwrap_or_default')).toBe(true);
expect(isBuiltInOrNoise('ok')).toBe(true);
expect(isBuiltInOrNoise('err')).toBe(true);
expect(isBuiltInOrNoise('is_ok')).toBe(true);
expect(isBuiltInOrNoise('is_err')).toBe(true);
expect(isBuiltInOrNoise('map_err')).toBe(true);
expect(isBuiltInOrNoise('and_then')).toBe(true);
expect(isBuiltInOrNoise('or_else')).toBe(true);
expect(rust.isBuiltInName('unwrap')).toBe(true);
expect(rust.isBuiltInName('expect')).toBe(true);
expect(rust.isBuiltInName('unwrap_or')).toBe(true);
expect(rust.isBuiltInName('unwrap_or_else')).toBe(true);
expect(rust.isBuiltInName('unwrap_or_default')).toBe(true);
expect(rust.isBuiltInName('ok')).toBe(true);
expect(rust.isBuiltInName('err')).toBe(true);
expect(rust.isBuiltInName('is_ok')).toBe(true);
expect(rust.isBuiltInName('is_err')).toBe(true);
expect(rust.isBuiltInName('map_err')).toBe(true);
expect(rust.isBuiltInName('and_then')).toBe(true);
expect(rust.isBuiltInName('or_else')).toBe(true);
});
it('filters trait conversion methods', () => {
expect(isBuiltInOrNoise('clone')).toBe(true);
expect(isBuiltInOrNoise('to_string')).toBe(true);
expect(isBuiltInOrNoise('to_owned')).toBe(true);
expect(isBuiltInOrNoise('into')).toBe(true);
expect(isBuiltInOrNoise('from')).toBe(true);
expect(isBuiltInOrNoise('as_ref')).toBe(true);
expect(isBuiltInOrNoise('as_mut')).toBe(true);
expect(rust.isBuiltInName('clone')).toBe(true);
expect(rust.isBuiltInName('to_string')).toBe(true);
expect(rust.isBuiltInName('to_owned')).toBe(true);
expect(rust.isBuiltInName('into')).toBe(true);
expect(rust.isBuiltInName('from')).toBe(true);
expect(rust.isBuiltInName('as_ref')).toBe(true);
expect(rust.isBuiltInName('as_mut')).toBe(true);
});
it('filters iterator methods', () => {
expect(isBuiltInOrNoise('iter')).toBe(true);
expect(isBuiltInOrNoise('into_iter')).toBe(true);
expect(isBuiltInOrNoise('collect')).toBe(true);
expect(isBuiltInOrNoise('fold')).toBe(true);
expect(isBuiltInOrNoise('for_each')).toBe(true);
expect(rust.isBuiltInName('iter')).toBe(true);
expect(rust.isBuiltInName('into_iter')).toBe(true);
expect(rust.isBuiltInName('collect')).toBe(true);
expect(rust.isBuiltInName('fold')).toBe(true);
expect(rust.isBuiltInName('for_each')).toBe(true);
});
it('filters collection methods', () => {
expect(isBuiltInOrNoise('len')).toBe(true);
expect(isBuiltInOrNoise('is_empty')).toBe(true);
expect(isBuiltInOrNoise('push')).toBe(true);
expect(isBuiltInOrNoise('pop')).toBe(true);
expect(isBuiltInOrNoise('insert')).toBe(true);
expect(isBuiltInOrNoise('remove')).toBe(true);
expect(isBuiltInOrNoise('contains')).toBe(true);
expect(rust.isBuiltInName('len')).toBe(true);
expect(rust.isBuiltInName('is_empty')).toBe(true);
expect(rust.isBuiltInName('push')).toBe(true);
expect(rust.isBuiltInName('pop')).toBe(true);
expect(rust.isBuiltInName('insert')).toBe(true);
expect(rust.isBuiltInName('remove')).toBe(true);
expect(rust.isBuiltInName('contains')).toBe(true);
});
it('filters macro-like and panic functions', () => {
expect(isBuiltInOrNoise('format')).toBe(true);
expect(isBuiltInOrNoise('panic')).toBe(true);
expect(isBuiltInOrNoise('unreachable')).toBe(true);
expect(isBuiltInOrNoise('todo')).toBe(true);
expect(isBuiltInOrNoise('unimplemented')).toBe(true);
expect(isBuiltInOrNoise('vec')).toBe(true);
expect(isBuiltInOrNoise('println')).toBe(true);
expect(isBuiltInOrNoise('eprintln')).toBe(true);
expect(isBuiltInOrNoise('dbg')).toBe(true);
expect(rust.isBuiltInName('format')).toBe(true);
expect(rust.isBuiltInName('panic')).toBe(true);
expect(rust.isBuiltInName('unreachable')).toBe(true);
expect(rust.isBuiltInName('todo')).toBe(true);
expect(rust.isBuiltInName('unimplemented')).toBe(true);
expect(rust.isBuiltInName('vec')).toBe(true);
expect(rust.isBuiltInName('println')).toBe(true);
expect(rust.isBuiltInName('eprintln')).toBe(true);
expect(rust.isBuiltInName('dbg')).toBe(true);
});
it('filters sync primitives', () => {
expect(isBuiltInOrNoise('lock')).toBe(true);
expect(isBuiltInOrNoise('try_lock')).toBe(true);
expect(isBuiltInOrNoise('spawn')).toBe(true);
expect(isBuiltInOrNoise('join')).toBe(true);
expect(isBuiltInOrNoise('sleep')).toBe(true);
expect(rust.isBuiltInName('lock')).toBe(true);
expect(rust.isBuiltInName('try_lock')).toBe(true);
expect(rust.isBuiltInName('spawn')).toBe(true);
expect(rust.isBuiltInName('join')).toBe(true);
expect(rust.isBuiltInName('sleep')).toBe(true);
});
it('filters enum constructors', () => {
expect(isBuiltInOrNoise('Some')).toBe(true);
expect(isBuiltInOrNoise('None')).toBe(true);
expect(isBuiltInOrNoise('Ok')).toBe(true);
expect(isBuiltInOrNoise('Err')).toBe(true);
expect(rust.isBuiltInName('Some')).toBe(true);
expect(rust.isBuiltInName('None')).toBe(true);
expect(rust.isBuiltInName('Ok')).toBe(true);
expect(rust.isBuiltInName('Err')).toBe(true);
});
it('does not filter user-defined Rust functions', () => {
expect(isBuiltInOrNoise('process_request')).toBe(false);
expect(isBuiltInOrNoise('handle_connection')).toBe(false);
expect(isBuiltInOrNoise('build_response')).toBe(false);
expect(rust.isBuiltInName('process_request')).toBe(false);
expect(rust.isBuiltInName('handle_connection')).toBe(false);
expect(rust.isBuiltInName('build_response')).toBe(false);
});
});
describe('C#/.NET', () => {
it('filters Console I/O', () => {
expect(isBuiltInOrNoise('Console')).toBe(true);
expect(isBuiltInOrNoise('WriteLine')).toBe(true);
expect(isBuiltInOrNoise('ReadLine')).toBe(true);
expect(cs.isBuiltInName('Console')).toBe(true);
expect(cs.isBuiltInName('WriteLine')).toBe(true);
expect(cs.isBuiltInName('ReadLine')).toBe(true);
});
it('filters LINQ methods', () => {
expect(isBuiltInOrNoise('Where')).toBe(true);
expect(isBuiltInOrNoise('Select')).toBe(true);
expect(isBuiltInOrNoise('GroupBy')).toBe(true);
expect(isBuiltInOrNoise('OrderBy')).toBe(true);
expect(isBuiltInOrNoise('FirstOrDefault')).toBe(true);
expect(isBuiltInOrNoise('ToList')).toBe(true);
expect(cs.isBuiltInName('Where')).toBe(true);
expect(cs.isBuiltInName('Select')).toBe(true);
expect(cs.isBuiltInName('GroupBy')).toBe(true);
expect(cs.isBuiltInName('OrderBy')).toBe(true);
expect(cs.isBuiltInName('FirstOrDefault')).toBe(true);
expect(cs.isBuiltInName('ToList')).toBe(true);
});
it('filters Task async methods', () => {
expect(isBuiltInOrNoise('Task')).toBe(true);
expect(isBuiltInOrNoise('Run')).toBe(true);
expect(isBuiltInOrNoise('WhenAll')).toBe(true);
expect(isBuiltInOrNoise('ConfigureAwait')).toBe(true);
expect(cs.isBuiltInName('Task')).toBe(true);
expect(cs.isBuiltInName('Run')).toBe(true);
expect(cs.isBuiltInName('WhenAll')).toBe(true);
expect(cs.isBuiltInName('ConfigureAwait')).toBe(true);
});
it('filters Object base methods', () => {
expect(isBuiltInOrNoise('ToString')).toBe(true);
expect(isBuiltInOrNoise('GetType')).toBe(true);
expect(isBuiltInOrNoise('Equals')).toBe(true);
expect(isBuiltInOrNoise('GetHashCode')).toBe(true);
expect(cs.isBuiltInName('ToString')).toBe(true);
expect(cs.isBuiltInName('GetType')).toBe(true);
expect(cs.isBuiltInName('Equals')).toBe(true);
expect(cs.isBuiltInName('GetHashCode')).toBe(true);
});
});
describe('user-defined functions', () => {
it('does not filter custom function names', () => {
expect(isBuiltInOrNoise('myCustomFunction')).toBe(false);
expect(isBuiltInOrNoise('processData')).toBe(false);
expect(isBuiltInOrNoise('handleUserRequest')).toBe(false);
expect(js.isBuiltInName('myCustomFunction')).toBe(false);
expect(py.isBuiltInName('processData')).toBe(false);
expect(rust.isBuiltInName('handleUserRequest')).toBe(false);
});
});
});

View file

@ -0,0 +1,92 @@
import { describe, it, expect } from 'vitest';
import { getProvider } from '../../src/core/ingestion/languages/index.js';
import { SupportedLanguages } from '../../src/config/supported-languages.js';
const isBuiltIn = (name: string, lang: SupportedLanguages) => getProvider(lang).isBuiltInName(name);
describe('isBuiltInOrNoise (per-language)', () => {
describe('language-specific filtering', () => {
it('filters console for JS but not Python', () => {
expect(isBuiltIn('console', SupportedLanguages.JavaScript)).toBe(true);
expect(isBuiltIn('console', SupportedLanguages.Python)).toBe(false);
});
it('filters println for Kotlin but not Java', () => {
expect(isBuiltIn('println', SupportedLanguages.Kotlin)).toBe(true);
expect(isBuiltIn('println', SupportedLanguages.Java)).toBe(false);
});
it('filters malloc for C but not JavaScript', () => {
expect(isBuiltIn('malloc', SupportedLanguages.C)).toBe(true);
expect(isBuiltIn('malloc', SupportedLanguages.JavaScript)).toBe(false);
});
it('filters setState for Dart but not TypeScript', () => {
expect(isBuiltIn('setState', SupportedLanguages.Dart)).toBe(true);
expect(isBuiltIn('setState', SupportedLanguages.TypeScript)).toBe(false);
});
it('filters unwrap for Rust but not Go', () => {
expect(isBuiltIn('unwrap', SupportedLanguages.Rust)).toBe(true);
expect(isBuiltIn('unwrap', SupportedLanguages.Go)).toBe(false);
});
it('filters puts for Ruby but not PHP', () => {
expect(isBuiltIn('puts', SupportedLanguages.Ruby)).toBe(true);
expect(isBuiltIn('puts', SupportedLanguages.PHP)).toBe(false);
});
it('filters echo for PHP but not Python', () => {
expect(isBuiltIn('echo', SupportedLanguages.PHP)).toBe(true);
expect(isBuiltIn('echo', SupportedLanguages.Python)).toBe(false);
});
it('filters NSLog for Swift but not C', () => {
expect(isBuiltIn('NSLog', SupportedLanguages.Swift)).toBe(true);
expect(isBuiltIn('NSLog', SupportedLanguages.C)).toBe(false);
});
it('filters ToString for C# but not Rust', () => {
expect(isBuiltIn('ToString', SupportedLanguages.CSharp)).toBe(true);
expect(isBuiltIn('ToString', SupportedLanguages.Rust)).toBe(false);
});
});
describe('cross-language pollution eliminated', () => {
it('close is filtered for C# but not C (POSIX)', () => {
expect(isBuiltIn('Close', SupportedLanguages.CSharp)).toBe(true);
expect(isBuiltIn('close', SupportedLanguages.C)).toBe(false);
});
it('then/catch are JS-specific, not filtered for Rust', () => {
expect(isBuiltIn('then', SupportedLanguages.JavaScript)).toBe(true);
expect(isBuiltIn('catch', SupportedLanguages.JavaScript)).toBe(true);
expect(isBuiltIn('then', SupportedLanguages.Rust)).toBe(false);
});
it('emit is Kotlin-specific, not filtered for Java', () => {
expect(isBuiltIn('emit', SupportedLanguages.Kotlin)).toBe(true);
expect(isBuiltIn('emit', SupportedLanguages.Java)).toBe(false);
});
});
describe('languages without builtInNames', () => {
it('Java has no language-specific noise', () => {
expect(isBuiltIn('System', SupportedLanguages.Java)).toBe(false);
expect(isBuiltIn('println', SupportedLanguages.Java)).toBe(false);
});
it('Go has no language-specific noise', () => {
expect(isBuiltIn('fmt', SupportedLanguages.Go)).toBe(false);
expect(isBuiltIn('Println', SupportedLanguages.Go)).toBe(false);
});
});
describe('domain names not filtered', () => {
it('does not filter arbitrary names', () => {
expect(isBuiltIn('processOrder', SupportedLanguages.TypeScript)).toBe(false);
expect(isBuiltIn('UserService', SupportedLanguages.Java)).toBe(false);
expect(isBuiltIn('handle_request', SupportedLanguages.Rust)).toBe(false);
});
});
});