diff --git a/gitnexus/src/core/ingestion/call-processor.ts b/gitnexus/src/core/ingestion/call-processor.ts index c39c85ffc..f0599a748 100644 --- a/gitnexus/src/core/ingestion/call-processor.ts +++ b/gitnexus/src/core/ingestion/call-processor.ts @@ -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); diff --git a/gitnexus/src/core/ingestion/language-provider.ts b/gitnexus/src/core/ingestion/language-provider.ts index b7449d33f..8243598ae 100644 --- a/gitnexus/src/core/ingestion/language-provider.ts +++ b/gitnexus/src/core/ingestion/language-provider.ts @@ -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; } /** Runtime type — same as LanguageProviderConfig but with defaults guaranteed present. */ @@ -119,6 +124,8 @@ export interface LanguageProvider extends Omit boolean; } const DEFAULTS: Pick = { @@ -129,5 +136,10 @@ const DEFAULTS: Pick builtIns.has(name) : () => false, + }; } diff --git a/gitnexus/src/core/ingestion/languages/c-cpp.ts b/gitnexus/src/core/ingestion/languages/c-cpp.ts index 6881a9ee4..feaca2238 100644 --- a/gitnexus/src/core/ingestion/languages/c-cpp.ts +++ b/gitnexus/src/core/ingestion/languages/c-cpp.ts @@ -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 = 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 = (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, }); diff --git a/gitnexus/src/core/ingestion/languages/csharp.ts b/gitnexus/src/core/ingestion/languages/csharp.ts index 707e225eb..313eecb5a 100644 --- a/gitnexus/src/core/ingestion/languages/csharp.ts +++ b/gitnexus/src/core/ingestion/languages/csharp.ts @@ -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 = 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, }); diff --git a/gitnexus/src/core/ingestion/languages/dart.ts b/gitnexus/src/core/ingestion/languages/dart.ts index 8b4dd3ee5..79fa3d20a 100644 --- a/gitnexus/src/core/ingestion/languages/dart.ts +++ b/gitnexus/src/core/ingestion/languages/dart.ts @@ -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 = 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, }); diff --git a/gitnexus/src/core/ingestion/languages/kotlin.ts b/gitnexus/src/core/ingestion/languages/kotlin.ts index a70153adb..97d77b42d 100644 --- a/gitnexus/src/core/ingestion/languages/kotlin.ts +++ b/gitnexus/src/core/ingestion/languages/kotlin.ts @@ -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 = 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'; diff --git a/gitnexus/src/core/ingestion/languages/php.ts b/gitnexus/src/core/ingestion/languages/php.ts index 584668140..41426b20c 100644 --- a/gitnexus/src/core/ingestion/languages/php.ts +++ b/gitnexus/src/core/ingestion/languages/php.ts @@ -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 = 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, }); diff --git a/gitnexus/src/core/ingestion/languages/python.ts b/gitnexus/src/core/ingestion/languages/python.ts index 49154e7fa..9f7992ab8 100644 --- a/gitnexus/src/core/ingestion/languages/python.ts +++ b/gitnexus/src/core/ingestion/languages/python.ts @@ -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 = 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, }); diff --git a/gitnexus/src/core/ingestion/languages/ruby.ts b/gitnexus/src/core/ingestion/languages/ruby.ts index 8c086175d..217f87bbf 100644 --- a/gitnexus/src/core/ingestion/languages/ruby.ts +++ b/gitnexus/src/core/ingestion/languages/ruby.ts @@ -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 = 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, }); diff --git a/gitnexus/src/core/ingestion/languages/rust.ts b/gitnexus/src/core/ingestion/languages/rust.ts index 71a2448f6..2119f5444 100644 --- a/gitnexus/src/core/ingestion/languages/rust.ts +++ b/gitnexus/src/core/ingestion/languages/rust.ts @@ -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 = 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, }); diff --git a/gitnexus/src/core/ingestion/languages/swift.ts b/gitnexus/src/core/ingestion/languages/swift.ts index 8d8af74e2..df0635a78 100644 --- a/gitnexus/src/core/ingestion/languages/swift.ts +++ b/gitnexus/src/core/ingestion/languages/swift.ts @@ -101,6 +101,34 @@ function wireSwiftImplicitImports( } } +const BUILT_INS: ReadonlySet = 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, }); diff --git a/gitnexus/src/core/ingestion/languages/typescript.ts b/gitnexus/src/core/ingestion/languages/typescript.ts index 7ec116c7a..d1e697b1d 100644 --- a/gitnexus/src/core/ingestion/languages/typescript.ts +++ b/gitnexus/src/core/ingestion/languages/typescript.ts @@ -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 = 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, }); diff --git a/gitnexus/src/core/ingestion/tree-sitter-queries.ts b/gitnexus/src/core/ingestion/tree-sitter-queries.ts index 9b7b45693..42e995a03 100644 --- a/gitnexus/src/core/ingestion/tree-sitter-queries.ts +++ b/gitnexus/src/core/ingestion/tree-sitter-queries.ts @@ -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 diff --git a/gitnexus/src/core/ingestion/type-env.ts b/gitnexus/src/core/ingestion/type-env.ts index af2c43488..a694c2447 100644 --- a/gitnexus/src/core/ingestion/type-env.ts +++ b/gitnexus/src/core/ingestion/type-env.ts @@ -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) diff --git a/gitnexus/src/core/ingestion/utils/noise-filter.ts b/gitnexus/src/core/ingestion/utils/noise-filter.ts deleted file mode 100644 index b1516b4d9..000000000 --- a/gitnexus/src/core/ingestion/utils/noise-filter.ts +++ /dev/null @@ -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); diff --git a/gitnexus/src/core/ingestion/workers/parse-worker.ts b/gitnexus/src/core/ingestion/workers/parse-worker.ts index 1f0e72436..d24fc07fc 100644 --- a/gitnexus/src/core/ingestion/workers/parse-worker.ts +++ b/gitnexus/src/core/ingestion/workers/parse-worker.ts @@ -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); diff --git a/gitnexus/test/integration/resolvers/java.test.ts b/gitnexus/test/integration/resolvers/java.test.ts index 89a8d89fd..a3d0a81e5 100644 --- a/gitnexus/test/integration/resolvers/java.test.ts +++ b/gitnexus/test/integration/resolvers/java.test.ts @@ -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', ]); }); diff --git a/gitnexus/test/unit/ingestion-utils.test.ts b/gitnexus/test/unit/ingestion-utils.test.ts index ab3ff06d7..20b081757 100644 --- a/gitnexus/test/unit/ingestion-utils.test.ts +++ b/gitnexus/test/unit/ingestion-utils.test.ts @@ -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); }); }); }); diff --git a/gitnexus/test/unit/noise-filter.test.ts b/gitnexus/test/unit/noise-filter.test.ts new file mode 100644 index 000000000..3b1b0085d --- /dev/null +++ b/gitnexus/test/unit/noise-filter.test.ts @@ -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); + }); + }); +});