Files
deusdata--codebase-memory-mcp/internal/cbm/preprocessor.cpp
Martin Vogel a8daedf759 Add C/C++ hybrid LSP resolver with type-aware call resolution
Implement a tree-sitter-based type evaluator for C/C++ that resolves
method calls, template instantiations, and cross-file references without
requiring a full compiler. Covers ~95% of C and ~93% of moderate C++.

Key features:
- Type-aware overload resolution scoring by parameter types
- Lambda return type inference (trailing return + body deduction)
- Inline namespace normalization (__1, __cxx11, __gnu_cxx)
- Implicit conversions (char*->string, numeric promotions)
- Virtual dispatch with override preference
- CRTP detection and self-type resolution
- Range-for iterator protocol (maps, custom containers)
- Smart pointer arrow dispatch (unique_ptr, shared_ptr)
- Dependent member calls in templates via pending call resolution
- Explicit template argument substitution (make_shared<T>)
- Nested class scope resolution
- C/C++ stdlib type stubs (containers, strings, I/O, smart ptrs)
- Cross-file LSP with compile_commands.json support
- C/C++ parameter extraction via declarator chain traversal

63+ test cases covering all resolution strategies.
2026-03-13 00:07:28 +01:00

96 lines
2.9 KiB
C++

// Unity build: include simplecpp implementation directly since CGo only
// compiles .cpp files from the immediate package directory, not subdirs.
#include "vendored/simplecpp/simplecpp.cpp"
#include "preprocessor.h"
#include "vendored/simplecpp/simplecpp.h"
#include <sstream>
#include <string>
#include <vector>
#include <cstring>
#include <cstdlib>
extern "C" {
char* cbm_preprocess(
const char* source, int source_len,
const char* filename,
const char** extra_defines,
const char** include_paths,
int cpp_mode
) {
if (!source || source_len <= 0) return NULL;
// Fast-path: skip if no preprocessor directives worth expanding.
bool has_macros = false;
for (int i = 0; i < source_len - 1; i++) {
if (source[i] == '#') {
// Skip whitespace after #
int j = i + 1;
while (j < source_len && (source[j] == ' ' || source[j] == '\t')) j++;
int remaining = source_len - j;
if (remaining >= 6 && strncmp(source + j, "define", 6) == 0) {
has_macros = true;
break;
}
if (remaining >= 5 && strncmp(source + j, "ifdef", 5) == 0) {
has_macros = true;
break;
}
if (remaining >= 6 && strncmp(source + j, "ifndef", 6) == 0) {
has_macros = true;
break;
}
if (remaining >= 3 && strncmp(source + j, "if ", 3) == 0) {
has_macros = true;
break;
}
}
}
if (!has_macros) return NULL; // NULL = no expansion needed, use original
try {
simplecpp::DUI dui;
if (extra_defines) {
for (int i = 0; extra_defines[i]; i++)
dui.defines.push_back(extra_defines[i]);
}
if (include_paths) {
for (int i = 0; include_paths[i]; i++)
dui.includePaths.push_back(include_paths[i]);
}
dui.std = cpp_mode ? "c++17" : "c11";
std::string src(source, source_len);
std::istringstream istr(src);
std::vector<std::string> files;
files.push_back(filename ? filename : "<input>");
simplecpp::TokenList rawtokens(istr, files, files[0]);
simplecpp::TokenList output(files);
simplecpp::FileDataCache filedata = simplecpp::load(rawtokens, files, dui);
simplecpp::preprocess(output, rawtokens, files, filedata, dui);
std::string result = output.stringify();
// Clean up loaded file data
simplecpp::cleanup(filedata);
char* out = (char*)malloc(result.size() + 1);
if (!out) return NULL;
memcpy(out, result.c_str(), result.size() + 1);
return out;
} catch (...) {
// Graceful fallback: return NULL = use original source
return NULL;
}
}
void cbm_preprocess_free(char* expanded) {
free(expanded);
}
} // extern "C"