1//===- SSAFAnalysesCommon.cpp ---------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "SSAFAnalysesCommon.h"
10#include "clang/AST/ASTContext.h"
11#include "clang/AST/Decl.h"
12#include "clang/AST/DeclObjC.h"
13#include "clang/AST/DynamicRecursiveASTVisitor.h"
14#include "clang/AST/ExprCXX.h"
15#include <set>
16
17using namespace clang;
18
19std::string ssaf::describeJSONValue(const llvm::json::Value &V) {
20 return llvm::formatv(Fmt: "{0:2}", Vals: V).str();
21}
22
23std::string ssaf::describeJSONValue(const llvm::json::Array &A) {
24 return llvm::formatv(Fmt: "array of size {0}", Vals: A.size()).str();
25}
26
27std::string ssaf::describeJSONValue(const llvm::json::Object &O) {
28 return llvm::formatv(Fmt: "an object of {0} key(s)", Vals: O.size()).str();
29}
30
31namespace {
32// Traverses the AST and finds contributors.
33class ContributorFinder : public DynamicRecursiveASTVisitor {
34public:
35 std::set<const NamedDecl *> Contributors;
36
37 ContributorFinder() {
38 ShouldVisitTemplateInstantiations = true;
39 ShouldVisitImplicitCode = false;
40 }
41
42 bool VisitFunctionDecl(FunctionDecl *D) override {
43 Contributors.insert(x: D);
44 return true;
45 }
46
47 bool VisitRecordDecl(RecordDecl *D) override {
48 Contributors.insert(x: D);
49 return true;
50 }
51
52 bool VisitVarDecl(VarDecl *D) override {
53 DeclContext *DC = D->getDeclContext();
54
55 // Collects Decl for global variables or static data members:
56 if (DC->isFileContext() || D->isStaticDataMember())
57 Contributors.insert(x: D);
58 return true;
59 }
60
61 bool VisitLambdaExpr(LambdaExpr *L) override {
62 // TraverseLambdaExpr directly visits the body stmt, skipping the
63 // CXXMethodDecl, which is a contributor that needs to be collected.
64 VisitFunctionDecl(D: L->getCallOperator());
65 return true;
66 }
67};
68
69/// An AST visitor that skips the root node's strict-descendants that are
70/// callable Decls and record Decls, because those are separate contributors.
71///
72/// Clients need to implement their own "MatchAction", which is a function that
73/// takes a `DynTypedNode`, decides if the node matches and performs any further
74/// callback actions.
75/// ContributorFactFinder takes a reference to a "MatchAction". It does not own
76/// the "MatchAction", which is usually stateful and may own containers.
77class ContributorFactFinder : public DynamicRecursiveASTVisitor {
78 llvm::function_ref<void(const DynTypedNode &)> MatchActionRef;
79 const NamedDecl *RootDecl = nullptr;
80
81 template <typename NodeTy> void match(const NodeTy &Node) {
82 MatchActionRef(DynTypedNode::create(Node));
83 }
84
85public:
86 ContributorFactFinder(
87 llvm::function_ref<void(const DynTypedNode &)> MatchActionRef)
88 : MatchActionRef(MatchActionRef) {
89 ShouldVisitTemplateInstantiations = true;
90 ShouldVisitImplicitCode = false;
91 }
92
93 // The entry point:
94 void findMatches(const NamedDecl *Contributor) {
95 RootDecl = Contributor;
96 TraverseDecl(Node: const_cast<NamedDecl *>(Contributor));
97 }
98
99 bool TraverseDecl(Decl *Node) override {
100 if (!Node)
101 return true;
102 // To skip callables:
103 if (Node != RootDecl &&
104 isa<FunctionDecl, BlockDecl, ObjCMethodDecl, RecordDecl>(Val: Node))
105 return true;
106 match(Node: *Node);
107 return DynamicRecursiveASTVisitor::TraverseDecl(D: Node);
108 }
109
110 bool TraverseStmt(Stmt *Node) override {
111 if (!Node)
112 return true;
113 match(Node: *Node);
114 return DynamicRecursiveASTVisitor::TraverseStmt(S: Node);
115 }
116
117 bool TraverseLambdaExpr(LambdaExpr *L) override {
118 // TODO: lambda captures of pointer variables (by copy or by reference)
119 // are currently not tracked. Each capture initializes an implicit closure
120 // field from the captured variable, which constitutes a pointer assignment
121 // edge that should be recorded here.
122 return true; // Skip lambda as it is a callable.
123 }
124};
125} // namespace
126
127void ssaf::findContributors(
128 ASTContext &Ctx,
129 llvm::DenseMap<const NamedDecl *, std::vector<const NamedDecl *>>
130 &Contributors) {
131 ContributorFinder Finder;
132 Finder.TraverseAST(AST&: Ctx);
133 for (const NamedDecl *C : Finder.Contributors)
134 Contributors[cast<NamedDecl>(Val: C->getCanonicalDecl())].push_back(x: C);
135}
136
137void ssaf::findMatchesIn(
138 const NamedDecl *Contributor,
139 llvm::function_ref<void(const DynTypedNode &)> MatchActionRef) {
140 ContributorFactFinder{MatchActionRef}.findMatches(Contributor);
141}
142
143llvm::Error clang::ssaf::makeEntityNameErr(clang::ASTContext &Ctx,
144 const clang::NamedDecl *D) {
145 return makeErrAtNode(Ctx, N: D, Fmt: "failed to create entity name for %s",
146 Args: D->getNameAsString().data());
147}
148