#pragma once #include "ast/Module.h" #include "ast/Function.h" #include "ast/ExternalModule.h" #include "ast/TypeSignature.h" #include "ast/Expression.h" #include #include #include #include struct LibraryPolicyResult { bool ok = true; std::string warning; std::string error; std::vector unknownFunctions; }; static inline void collectFunctionCalls(ASTNode* node, std::vector& out) { if (!node) return; if (node->conceptType == "FunctionCall") { auto* call = static_cast(node); if (!call->functionName.empty()) out.push_back(call->functionName); } for (auto* child : node->allChildren()) { collectFunctionCalls(child, out); } } static inline bool matchesAllowed(const std::unordered_set& allowed, const std::string& name) { if (allowed.count(name) > 0) return true; for (const auto& item : allowed) { if (item.size() > name.size()) { if (item.rfind("." + name) == item.size() - name.size() - 1) return true; if (item.rfind("::" + name) == item.size() - name.size() - 2) return true; } } return false; } static inline std::vector collectAllowedFunctions(Module* ast) { std::vector out; if (!ast) return out; for (auto* fnNode : ast->getChildren("functions")) { if (fnNode->conceptType != "Function") continue; auto* fn = static_cast(fnNode); if (!fn->name.empty()) out.push_back(fn->name); } for (auto* extNode : ast->getChildren("externalModules")) { if (extNode->conceptType != "ExternalModule") continue; auto* ext = static_cast(extNode); for (auto* sigNode : ext->getChildren("signatures")) { if (sigNode->conceptType != "TypeSignature") continue; auto* sig = static_cast(sigNode); if (!sig->name.empty()) out.push_back(sig->name); } } return out; } static inline LibraryPolicyResult checkMutationLibraryPolicy(ASTNode* node, Module* ast, bool preferImports, bool strictMode) { LibraryPolicyResult res; if (!preferImports) return res; auto allowedList = collectAllowedFunctions(ast); std::unordered_set allowed(allowedList.begin(), allowedList.end()); std::vector calls; collectFunctionCalls(node, calls); std::vector unknown; for (const auto& name : calls) { if (!matchesAllowed(allowed, name)) { if (std::find(unknown.begin(), unknown.end(), name) == unknown.end()) { unknown.push_back(name); } } } if (!unknown.empty()) { res.unknownFunctions = unknown; std::string alt; for (size_t i = 0; i < std::min(allowedList.size(), 5); ++i) { if (!alt.empty()) alt += ", "; alt += allowedList[i]; } std::string message = "References functions outside imports: "; for (size_t i = 0; i < unknown.size(); ++i) { if (i > 0) message += ", "; message += unknown[i]; } if (!alt.empty()) message += " | available: " + alt; if (strictMode) { res.ok = false; res.error = message; } else { res.warning = message; } } return res; }