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 "clang/Basic/SourceManager.h"
16#include "clang/Frontend/SSAFOptions.h"
17#include "llvm/ADT/SetVector.h"
18
19using namespace clang;
20using namespace ssaf;
21
22std::string ssaf::describeJSONValue(const llvm::json::Value &V) {
23 return llvm::formatv(Fmt: "{0:2}", Vals: V).str();
24}
25
26std::string ssaf::describeJSONValue(const llvm::json::Array &A) {
27 return llvm::formatv(Fmt: "array of size {0}", Vals: A.size()).str();
28}
29
30std::string ssaf::describeJSONValue(const llvm::json::Object &O) {
31 return llvm::formatv(Fmt: "an object of {0} key(s)", Vals: O.size()).str();
32}
33
34namespace {
35// Traverses the AST and finds contributors.
36class ContributorFinder : public DynamicRecursiveASTVisitor {
37public:
38 llvm::SetVector<const NamedDecl *> Contributors;
39 const SSAFOptions &Opts;
40
41 ContributorFinder(ASTContext &Ctx, const SSAFOptions &Opts,
42 bool ExtractFromSystemHeaders)
43 : Opts(Opts), Ctx(Ctx),
44 ExtractFromSystemHeaders(ExtractFromSystemHeaders) {
45 ShouldVisitTemplateInstantiations = true;
46 ShouldVisitImplicitCode = false;
47 }
48
49 bool VisitFunctionDecl(FunctionDecl *D) override {
50 if (!skipForSystemHeader(D))
51 Contributors.insert(X: D);
52 return true;
53 }
54
55 bool VisitRecordDecl(RecordDecl *D) override {
56 if (skipForSystemHeader(D))
57 return true;
58 Contributors.insert(X: D);
59 return true;
60 }
61
62 bool VisitVarDecl(VarDecl *D) override {
63 if (skipForSystemHeader(D))
64 return true;
65 DeclContext *DC = D->getDeclContext();
66
67 // Collects Decl for global variables or static data members:
68 if (DC->isFileContext() || D->isStaticDataMember()) {
69 Contributors.insert(X: D);
70 return true;
71 }
72
73 // Optionally include block-scope (function-local) variables. Parameters
74 // are intentionally skipped: they are exposed via their parent function's
75 // USR + a parameter-index suffix in getEntityName, so registering them as
76 // independent contributors would be redundant.
77 //
78 // FIXME: clang::index::generateUSRForDecl can produce non-unique or empty
79 // USRs for some local declaration shapes (e.g., locals of certain template
80 // instantiations). The current addEntity path returns std::nullopt when
81 // that happens and downstream extractors skip gracefully, so this is
82 // tolerated for now.
83 if (Opts.IncludeLocalEntities && !D->isImplicit() && !isa<ParmVarDecl>(Val: D) &&
84 DC->isFunctionOrMethod())
85 Contributors.insert(X: D);
86 return true;
87 }
88
89 bool VisitLambdaExpr(LambdaExpr *L) override {
90 return VisitFunctionDecl(D: L->getCallOperator());
91 }
92
93private:
94 bool skipForSystemHeader(const Decl *D) const {
95 if (ExtractFromSystemHeaders)
96 return false;
97 SourceLocation Loc = D->getLocation();
98 return Loc.isValid() && Ctx.getSourceManager().isInSystemHeader(Loc);
99 }
100
101 ASTContext &Ctx;
102 bool ExtractFromSystemHeaders;
103};
104
105/// An AST visitor that skips the root node's strict-descendants that are
106/// callable Decls and record Decls, because those are separate contributors.
107///
108/// Clients need to implement their own "MatchAction", which is a function that
109/// takes a `DynTypedNode`, decides if the node matches and performs any further
110/// callback actions.
111/// ContributorFactFinder takes a reference to a "MatchAction". It does not own
112/// the "MatchAction", which is usually stateful and may own containers.
113class ContributorFactFinder : public DynamicRecursiveASTVisitor {
114 llvm::function_ref<void(const DynTypedNode &)> MatchActionRef;
115 const NamedDecl *RootDecl = nullptr;
116
117 template <typename NodeTy> void match(const NodeTy &Node) {
118 MatchActionRef(DynTypedNode::create(Node));
119 }
120
121public:
122 ContributorFactFinder(
123 llvm::function_ref<void(const DynTypedNode &)> MatchActionRef)
124 : MatchActionRef(MatchActionRef) {
125 ShouldVisitTemplateInstantiations = true;
126 ShouldVisitImplicitCode = false;
127 }
128
129 // The entry point:
130 void findMatches(const NamedDecl *Contributor) {
131 RootDecl = Contributor;
132 TraverseDecl(Node: const_cast<NamedDecl *>(Contributor));
133 }
134
135 bool TraverseDecl(Decl *Node) override {
136 if (!Node)
137 return true;
138 // To skip callables:
139 if (Node != RootDecl &&
140 isa<FunctionDecl, BlockDecl, ObjCMethodDecl, RecordDecl>(Val: Node))
141 return true;
142 match(Node: *Node);
143 return DynamicRecursiveASTVisitor::TraverseDecl(D: Node);
144 }
145
146 bool TraverseStmt(Stmt *Node) override {
147 if (!Node)
148 return true;
149 match(Node: *Node);
150 return DynamicRecursiveASTVisitor::TraverseStmt(S: Node);
151 }
152
153 bool TraverseLambdaExpr(LambdaExpr *L) override {
154 // TODO: lambda captures of pointer variables (by copy or by reference)
155 // are currently not tracked. Each capture initializes an implicit closure
156 // field from the captured variable, which constitutes a pointer assignment
157 // edge that should be recorded here.
158 return true; // Skip lambda as it is a callable.
159 }
160};
161} // namespace
162
163void ssaf::findContributors(
164 ASTContext &Ctx, const SSAFOptions &Options,
165 llvm::DenseMap<const NamedDecl *, std::vector<const NamedDecl *>>
166 &Contributors,
167 bool ExtractFromSystemHeaders) {
168 ContributorFinder Finder{Ctx, Options, ExtractFromSystemHeaders};
169 Finder.TraverseAST(AST&: Ctx);
170 for (const NamedDecl *C : Finder.Contributors)
171 Contributors[cast<NamedDecl>(Val: C->getCanonicalDecl())].push_back(x: C);
172}
173
174void ssaf::findMatchesIn(
175 const NamedDecl *Contributor,
176 llvm::function_ref<void(const DynTypedNode &)> MatchActionRef) {
177 ContributorFactFinder{MatchActionRef}.findMatches(Contributor);
178}
179
180llvm::Error clang::ssaf::makeEntityNameErr(clang::ASTContext &Ctx,
181 const clang::NamedDecl *D) {
182 return makeErrAtNode(Ctx, N: D, Fmt: "failed to create entity name for %s",
183 Args: D->getNameAsString().data());
184}
185