libexpr/static-analyzer: init

Change-Id: I606718fbe33db1f6c00ca34f45c6553ce468398c
Signed-off-by: Raito Bezarius <raito@lix.systems>
This commit is contained in:
Raito Bezarius
2025-11-21 17:36:35 +01:00
parent 17879c9a83
commit a6f0e87fa8
2 changed files with 103 additions and 0 deletions
+75
View File
@@ -321,6 +321,74 @@ JSON printAttrPathToJson(const SymbolTable & symbols, const AttrPath & attrPath)
/* Computing levels/displacements for variables. */
namespace {
// This is a one-pass static analyzer for
// various topics.
struct StaticAnalyzer : ExprVisitor
{
std::set<Symbol> staticallyUsedVariables;
bool usedDynamicVariables = false;
StaticAnalyzer() {}
using ExprVisitor::visit;
void visit(ExprDebugFrame & e, std::unique_ptr<Expr> & ptr) override
{
visit(e.inner);
}
void visit(ExprLiteral & e, std::unique_ptr<Expr> & ptr) override {}
void visit(ExprVar & e, std::unique_ptr<Expr> & ptr) override
{
staticallyUsedVariables.insert(e.name);
}
void visit(ExprInheritFrom & e, std::unique_ptr<Expr> & ptr) override {}
void visit(ExprSelect & e, std::unique_ptr<Expr> & ptr) override
{
if (e.isDynamic()) {
usedDynamicVariables = true;
}
visit(e.def);
visit(e.e);
}
void visit(ExprOpHasAttr & e, std::unique_ptr<Expr> & ptr) override
{
if (e.isDynamic()) {
usedDynamicVariables = true;
}
visit(e.e);
}
void visit(ExprSet & e, std::unique_ptr<Expr> & ptr) override
{
// TODO: oh bro, we need to analyze dynamic attributes for their value expressions.
}
void visit(ExprList & e, std::unique_ptr<Expr> & ptr) override {}
void visit(ExprLambda & e, std::unique_ptr<Expr> & ptr) override {}
void visit(ExprCall & e, std::unique_ptr<Expr> & ptr) override {}
void visit(ExprLet & e, std::unique_ptr<Expr> & ptr) override {}
void visit(ExprWith & e, std::unique_ptr<Expr> & ptr) override {}
void visit(ExprIf & e, std::unique_ptr<Expr> & ptr) override {}
void visit(ExprAssert & e, std::unique_ptr<Expr> & ptr) override {}
void visit(ExprOpNot & e, std::unique_ptr<Expr> & ptr) override {}
#define BINOP(type) \
/* NOLINTNEXTLINE(bugprone-macro-parentheses) */ \
void visit(type & e, std::unique_ptr<Expr> & ptr) override \
{ \
visit(e.e1); \
visit(e.e2); \
}
BINOP(ExprOpEq)
BINOP(ExprOpNEq)
BINOP(ExprOpAnd)
BINOP(ExprOpOr)
BINOP(ExprOpImpl)
BINOP(ExprOpUpdate)
BINOP(ExprOpConcatLists)
#undef BINOP
void visit(ExprConcatStrings & e, std::unique_ptr<Expr> & ptr) override {}
void visit(ExprPos & e, std::unique_ptr<Expr> & ptr) override {}
void visit(ExprBlackHole & e, std::unique_ptr<Expr> & ptr) override {}
};
struct VarBinder : ExprVisitor
{
Evaluator & es;
@@ -590,6 +658,13 @@ void VarBinder::visit(ExprLambda & e, std::unique_ptr<Expr> & ptr)
{
withEnv(e.pattern->buildEnv(env.get()), [&] {
e.pattern->accept(*this);
/* TODO: If statically, e.body makes only use of some parameters and not the whole scope.
* We shouldn't have to keep around all the environment data which might contain trapped
* pointers. Analyze `e.body` and return its statically known set of used variables.
* */
DirectCallAnalyzer analyzer{es, env};
analyzer.visit(e.body);
e.shortcut = analyzer.shortcut;
visit(e.body);
});
}
+28
View File
@@ -292,6 +292,17 @@ struct ExprSelect : Expr
/** The path of attributes being selected. e.g. `bar.baz` in `foo.bar.baz.` */
AttrPath attrPath;
bool isDynamic() const
{
for (auto & name : attrPath) {
if (name.expr) {
return true;
}
}
return false;
}
ExprSelect(const PosIdx & pos, std::unique_ptr<Expr> e, AttrPath attrPath, std::unique_ptr<Expr> def) : Expr(pos), e(std::move(e)), def(std::move(def)), attrPath(std::move(attrPath)) { };
ExprSelect(const PosIdx & pos, std::unique_ptr<Expr> e, const PosIdx namePos, Symbol name) : Expr(pos), e(std::move(e)) { attrPath.push_back(AttrName(namePos, name)); };
JSON toJSON(const SymbolTable & symbols) const override;
@@ -303,6 +314,16 @@ struct ExprOpHasAttr : Expr
{
std::unique_ptr<Expr> e;
AttrPath attrPath;
bool isDynamic() const
{
for (auto & name : attrPath) {
if (name.expr) {
return true;
}
}
return false;
}
ExprOpHasAttr(const PosIdx & pos, std::unique_ptr<Expr> e, AttrPath attrPath) : Expr(pos), e(std::move(e)), attrPath(std::move(attrPath)) { };
JSON toJSON(const SymbolTable & symbols) const override;
void eval(EvalState & state, Env & env, Value & v) override;
@@ -479,6 +500,13 @@ struct ExprLambda : Expr
Symbol name;
std::unique_ptr<Pattern> pattern;
std::unique_ptr<Expr> body;
// This is a shortcut variant which
// exhausts the body further lambda constructions
// to transform x1: x2: …: xn: b
// into { x1, …, xn }: b
// This can be used when you know that you are
// passing all the arguments at once.
std::unique_ptr<ExprLambda> shortcut;
ExprLambda(PosIdx pos, std::unique_ptr<Pattern> pattern, std::unique_ptr<Expr> body)
: Expr(pos), pattern(std::move(pattern)), body(std::move(body))
{