1//===- ASTMapping.cpp - AST to SSAF Entity mapping ------------------------===//
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// This file implements utilities for mapping AST declarations to SSAF entities.
10//
11//===----------------------------------------------------------------------===//
12
13#include "clang/ScalableStaticAnalysis/Core/ASTEntityMapping.h"
14#include "clang/AST/Decl.h"
15#include "clang/ScalableStaticAnalysis/Core/EntityLinker/EntityLinker.h"
16#include "clang/ScalableStaticAnalysis/Core/Model/BuildNamespace.h"
17#include "clang/UnifiedSymbolResolution/USRGeneration.h"
18#include "llvm/ADT/SmallString.h"
19#include "llvm/Support/ErrorHandling.h"
20
21namespace clang::ssaf {
22
23std::optional<EntityName> getEntityName(const Decl *D) {
24 if (!D)
25 return std::nullopt;
26
27 if (D->isImplicit())
28 return std::nullopt;
29
30 if (isa<FunctionDecl>(Val: D) && cast<FunctionDecl>(Val: D)->getBuiltinID())
31 return std::nullopt;
32
33 if (!isa<FunctionDecl, ParmVarDecl, VarDecl, FieldDecl, RecordDecl>(Val: D))
34 return std::nullopt;
35
36 llvm::SmallString<16> Suffix;
37 const Decl *USRDecl = D;
38
39 // For parameters, use the parent function's USR with parameter index as
40 // suffix
41 if (const auto *PVD = dyn_cast<ParmVarDecl>(Val: D)) {
42 const auto *FD =
43 dyn_cast_or_null<FunctionDecl>(Val: PVD->getParentFunctionOrMethod());
44 if (!FD)
45 return std::nullopt;
46 USRDecl = FD;
47
48 const auto ParamIdx = PVD->getFunctionScopeIndex();
49 llvm::raw_svector_ostream OS(Suffix);
50 // Parameter uses function's USR with 1-based index as suffix
51 OS << (ParamIdx + 1);
52 }
53
54 llvm::SmallString<128> USRBuf;
55 if (clang::index::generateUSRForDecl(D: USRDecl, Buf&: USRBuf))
56 return std::nullopt;
57
58 if (USRBuf.empty())
59 return std::nullopt;
60
61 return EntityName(USRBuf.str(), Suffix, {});
62}
63
64std::optional<EntityName> getEntityNameForReturn(const FunctionDecl *FD) {
65 if (!FD)
66 return std::nullopt;
67
68 if (FD->isImplicit())
69 return std::nullopt;
70
71 if (FD->getBuiltinID())
72 return std::nullopt;
73
74 llvm::SmallString<128> USRBuf;
75 if (clang::index::generateUSRForDecl(D: FD, Buf&: USRBuf)) {
76 return std::nullopt;
77 }
78
79 if (USRBuf.empty())
80 return std::nullopt;
81
82 return EntityName(USRBuf.str(), /*Suffix=*/"0", /*Namespace=*/{});
83}
84
85EntityLinkageType getLinkageForDecl(const Decl *D) {
86 const auto *ND = dyn_cast<NamedDecl>(Val: D);
87 if (!ND)
88 return EntityLinkageType::None;
89
90 // Parameters have no linkage in C++, but SSAF needs them to inherit
91 // the external linkage from their parent functions.
92 // Here is why:
93 // SSAF treats parameters as entities and may not always associate them back
94 // to their parent functions. Therefore, it needs to identify parameters of
95 // functions with external linkage across different TUs. Treating them as
96 // having no linkage (as in C++) causes the same parameter in different TUs
97 // to be assigned different EntityIDs. As a result, the behavior of the
98 // parameter across multiple TUs cannot be correlated.
99 if (const auto *PVD = dyn_cast<ParmVarDecl>(Val: D)) {
100 if (const auto *FD = llvm::dyn_cast_or_null<FunctionDecl>(
101 Val: PVD->getParentFunctionOrMethod())) {
102 return getLinkageForDecl(D: FD);
103 }
104 }
105
106 switch (ND->getFormalLinkage()) {
107 case Linkage::Invalid: {
108 llvm_unreachable("Shouldn't be invalid");
109 }
110 case Linkage::None:
111 return EntityLinkageType::None;
112 case Linkage::Internal:
113 return EntityLinkageType::Internal;
114 case Linkage::UniqueExternal:
115 return EntityLinkageType::Internal;
116 case Linkage::VisibleNone:
117 return EntityLinkageType::Internal;
118 case Linkage::Module:
119 return EntityLinkageType::External;
120 case Linkage::External:
121 return EntityLinkageType::External;
122 }
123 llvm_unreachable("Unhandled clang::Linkage kind");
124}
125
126std::optional<EntityName>
127getQualifiedEntityName(const Decl *D, const NestedBuildNamespace &TUNamespace,
128 const NestedBuildNamespace &LUNamespace) {
129 std::optional<EntityName> Name = getEntityName(D);
130 if (!Name)
131 return std::nullopt;
132 return Name->makeQualified(Namespace: resolveNamespace(
133 LUNamespace, TUNamespace, /*EntityNamespace=*/{}, Linkage: getLinkageForDecl(D)));
134}
135
136std::optional<EntityName>
137getQualifiedEntityNameForReturn(const FunctionDecl *FD,
138 const NestedBuildNamespace &TUNamespace,
139 const NestedBuildNamespace &LUNamespace) {
140 std::optional<EntityName> Name = getEntityNameForReturn(FD);
141 if (!Name)
142 return std::nullopt;
143 return Name->makeQualified(Namespace: resolveNamespace(
144 LUNamespace, TUNamespace, /*EntityNamespace=*/{}, Linkage: getLinkageForDecl(D: FD)));
145}
146
147} // namespace clang::ssaf
148