1//===- EntitySourceLocationExtractor.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 "clang/AST/ASTContext.h"
10#include "clang/AST/Decl.h"
11#include "clang/AST/DeclCXX.h"
12#include "clang/AST/DynamicRecursiveASTVisitor.h"
13#include "clang/Basic/SourceLocation.h"
14#include "clang/Basic/SourceManager.h"
15#include "clang/ScalableStaticAnalysis/Analyses/SharedLexicalRepresentation/SharedLexicalRepresentation.h"
16#include "clang/ScalableStaticAnalysis/Core/Model/EntityId.h"
17#include "clang/ScalableStaticAnalysis/Core/TUSummary/ExtractorRegistry.h"
18#include "clang/ScalableStaticAnalysis/Core/TUSummary/TUSummaryBuilder.h"
19#include "clang/ScalableStaticAnalysis/Core/TUSummary/TUSummaryExtractor.h"
20#include "llvm/ADT/SmallString.h"
21#include "llvm/ADT/StringRef.h"
22#include "llvm/Support/FileSystem.h"
23#include "llvm/Support/IOSandbox.h"
24#include "llvm/Support/Path.h"
25
26#include <map>
27#include <memory>
28#include <optional>
29#include <utility>
30#include <vector>
31
32namespace clang::ssaf {
33extern EntitySourceLocationsSummary
34buildEntitySourceLocationsSummary(std::vector<SourceLocationRecord> Locs);
35} // namespace clang::ssaf
36
37namespace {
38using namespace clang;
39using namespace ssaf;
40
41/// Per-EntityId aggregation buffer used during one TU walk. Each visited
42/// declaration contributes one record; the buffer is committed once per
43/// EntityId at the end of the walk.
44using LocationMap = std::map<EntityId, std::vector<SourceLocationRecord>>;
45
46/// Compute a SourceLocationRecord from \p Loc. Returns std::nullopt for
47/// invalid / system-header / unreachable-on-disk paths; these are silently
48/// dropped per the spec.
49std::optional<SourceLocationRecord> makeRecord(SourceLocation Loc,
50 const SourceManager &SM) {
51 if (!Loc.isValid() || SM.isInSystemHeader(Loc))
52 return std::nullopt;
53
54 PresumedLoc PLoc = SM.getPresumedLoc(Loc);
55 if (PLoc.isInvalid())
56 return std::nullopt;
57
58 llvm::StringRef Filename(PLoc.getFilename());
59 if (Filename.empty())
60 return std::nullopt;
61
62 // Path canonicalization touches the filesystem, which is denied under the
63 // sandboxed compile drivers (e.g. xcodebuild). Reading source files the
64 // compiler is already reading is benign, so disable the sandbox for the
65 // duration of these calls.
66 llvm::SmallString<256> Abs(Filename);
67 llvm::SmallString<256> Real;
68 {
69 auto SandboxOff = llvm::sys::sandbox::scopedDisable();
70 if (!llvm::sys::path::is_absolute(path: Abs))
71 if (auto EC = llvm::sys::fs::make_absolute(path&: Abs))
72 return std::nullopt;
73 if (auto EC = llvm::sys::fs::real_path(path: Abs, output&: Real))
74 return std::nullopt;
75 }
76
77 SourceLocationRecord R;
78 R.FilePath = Real.str().str();
79 R.Line = PLoc.getLine();
80 R.Column = PLoc.getColumn();
81 return R;
82}
83
84void handleNamedDecl(TUSummaryExtractor &Extractor, const NamedDecl *D,
85 SourceLocation Loc, const SourceManager &SM,
86 LocationMap &Records) {
87 auto Rec = makeRecord(Loc, SM);
88 if (!Rec)
89 return;
90 std::optional<EntityId> Id = Extractor.addEntity(D);
91 if (!Id)
92 return;
93 Records[*Id].push_back(x: std::move(*Rec));
94}
95
96void handleFunction(TUSummaryExtractor &Extractor, const FunctionDecl *FD,
97 const SourceManager &SM, LocationMap &Records) {
98 handleNamedDecl(Extractor, D: FD, Loc: FD->getLocation(), SM, Records);
99
100 if (auto RetRec = makeRecord(Loc: FD->getReturnTypeSourceRange().getBegin(), SM))
101 if (auto RetId = Extractor.addEntityForReturn(FD))
102 Records[*RetId].push_back(x: std::move(*RetRec));
103
104 // The variadic '...' terminator is not represented by a ParmVarDecl in the
105 // AST, so this loop naturally skips it.
106 for (const ParmVarDecl *P : FD->parameters()) {
107 if (!P)
108 continue;
109 handleNamedDecl(Extractor, D: P, Loc: P->getTypeSpecStartLoc(), SM, Records);
110 }
111}
112
113/// AST visitor that records per-entity declaration source-locations as it
114/// walks. ParmVarDecls are visited as part of their parent FunctionDecl so
115/// the parameter-type-spec-start anchor is recorded once per redeclaration of
116/// the parent function.
117class EntityVisitor : public DynamicRecursiveASTVisitor {
118 TUSummaryExtractor &Extractor;
119 const SourceManager &SM;
120 LocationMap &Records;
121
122public:
123 EntityVisitor(TUSummaryExtractor &Extractor, const SourceManager &SM,
124 LocationMap &Records)
125 : Extractor(Extractor), SM(SM), Records(Records) {
126 ShouldVisitTemplateInstantiations = true;
127 ShouldVisitImplicitCode = false;
128 }
129
130 bool VisitFunctionDecl(FunctionDecl *FD) override {
131 if (FD)
132 handleFunction(Extractor, FD, SM, Records);
133 return true;
134 }
135
136 bool VisitVarDecl(VarDecl *VD) override {
137 if (VD && !isa<ParmVarDecl>(Val: VD))
138 handleNamedDecl(Extractor, D: VD, Loc: VD->getLocation(), SM, Records);
139 return true;
140 }
141
142 bool VisitFieldDecl(FieldDecl *FD) override {
143 if (FD)
144 handleNamedDecl(Extractor, D: FD, Loc: FD->getLocation(), SM, Records);
145 return true;
146 }
147
148 bool VisitRecordDecl(RecordDecl *RD) override {
149 if (RD)
150 handleNamedDecl(Extractor, D: RD, Loc: RD->getLocation(), SM, Records);
151 return true;
152 }
153};
154
155class EntitySourceLocationExtractor final : public TUSummaryExtractor {
156public:
157 using TUSummaryExtractor::TUSummaryExtractor;
158
159private:
160 void HandleTranslationUnit(ASTContext &Ctx) override;
161};
162
163void EntitySourceLocationExtractor::HandleTranslationUnit(ASTContext &Ctx) {
164 const SourceManager &SM = Ctx.getSourceManager();
165 LocationMap Records;
166 EntityVisitor(*this, SM, Records).TraverseAST(AST&: Ctx);
167
168 for (auto &[Id, Locs] : Records) {
169 auto Summary = std::make_unique<EntitySourceLocationsSummary>(
170 args: buildEntitySourceLocationsSummary(Locs: std::move(Locs)));
171 [[maybe_unused]] auto [Ignored, Inserted] =
172 SummaryBuilder.addSummary(Entity: Id, Data: std::move(Summary));
173 assert(Inserted &&
174 "EntitySourceLocations summary inserted twice for same EntityId");
175 }
176}
177
178} // namespace
179
180namespace clang::ssaf {
181// NOLINTNEXTLINE(misc-use-internal-linkage)
182volatile int EntitySourceLocationExtractorAnchorSource = 0;
183} // namespace clang::ssaf
184
185static clang::ssaf::TUSummaryExtractorRegistry::Add<
186 EntitySourceLocationExtractor>
187 RegisterExtractor(EntitySourceLocationsSummary::Name,
188 "Extract per-entity declaration source-locations");
189