[clang-tidy][NFC]remove deps of clang in clang tidy test (#116588)
[llvm-project.git] / clang / lib / AST / OSLog.cpp
blobb777d4d65b84d60f2a5124f704d419e733cb8fac
1 // TODO: header template
3 #include "clang/AST/OSLog.h"
4 #include "clang/AST/Attr.h"
5 #include "clang/AST/Decl.h"
6 #include "clang/AST/DeclCXX.h"
7 #include "clang/AST/ExprObjC.h"
8 #include "clang/AST/FormatString.h"
9 #include "clang/Basic/Builtins.h"
10 #include <optional>
12 using namespace clang;
14 using clang::analyze_os_log::OSLogBufferItem;
15 using clang::analyze_os_log::OSLogBufferLayout;
17 namespace {
18 class OSLogFormatStringHandler
19 : public analyze_format_string::FormatStringHandler {
20 private:
21 struct ArgData {
22 const Expr *E = nullptr;
23 std::optional<OSLogBufferItem::Kind> Kind;
24 std::optional<unsigned> Size;
25 std::optional<const Expr *> Count;
26 std::optional<const Expr *> Precision;
27 std::optional<const Expr *> FieldWidth;
28 unsigned char Flags = 0;
29 StringRef MaskType;
31 SmallVector<ArgData, 4> ArgsData;
32 ArrayRef<const Expr *> Args;
34 OSLogBufferItem::Kind
35 getKind(analyze_format_string::ConversionSpecifier::Kind K) {
36 switch (K) {
37 case clang::analyze_format_string::ConversionSpecifier::sArg: // "%s"
38 return OSLogBufferItem::StringKind;
39 case clang::analyze_format_string::ConversionSpecifier::SArg: // "%S"
40 return OSLogBufferItem::WideStringKind;
41 case clang::analyze_format_string::ConversionSpecifier::PArg: { // "%P"
42 return OSLogBufferItem::PointerKind;
43 case clang::analyze_format_string::ConversionSpecifier::ObjCObjArg: // "%@"
44 return OSLogBufferItem::ObjCObjKind;
45 case clang::analyze_format_string::ConversionSpecifier::PrintErrno: // "%m"
46 return OSLogBufferItem::ErrnoKind;
47 default:
48 return OSLogBufferItem::ScalarKind;
53 public:
54 OSLogFormatStringHandler(ArrayRef<const Expr *> Args) : Args(Args) {
55 ArgsData.reserve(Args.size());
58 bool HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier &FS,
59 const char *StartSpecifier, unsigned SpecifierLen,
60 const TargetInfo &) override {
61 if (!FS.consumesDataArgument() &&
62 FS.getConversionSpecifier().getKind() !=
63 clang::analyze_format_string::ConversionSpecifier::PrintErrno)
64 return true;
66 ArgsData.emplace_back();
67 unsigned ArgIndex = FS.getArgIndex();
68 if (ArgIndex < Args.size())
69 ArgsData.back().E = Args[ArgIndex];
71 // First get the Kind
72 ArgsData.back().Kind = getKind(FS.getConversionSpecifier().getKind());
73 if (ArgsData.back().Kind != OSLogBufferItem::ErrnoKind &&
74 !ArgsData.back().E) {
75 // missing argument
76 ArgsData.pop_back();
77 return false;
80 switch (FS.getConversionSpecifier().getKind()) {
81 case clang::analyze_format_string::ConversionSpecifier::sArg: // "%s"
82 case clang::analyze_format_string::ConversionSpecifier::SArg: { // "%S"
83 auto &precision = FS.getPrecision();
84 switch (precision.getHowSpecified()) {
85 case clang::analyze_format_string::OptionalAmount::NotSpecified: // "%s"
86 break;
87 case clang::analyze_format_string::OptionalAmount::Constant: // "%.16s"
88 ArgsData.back().Size = precision.getConstantAmount();
89 break;
90 case clang::analyze_format_string::OptionalAmount::Arg: // "%.*s"
91 ArgsData.back().Count = Args[precision.getArgIndex()];
92 break;
93 case clang::analyze_format_string::OptionalAmount::Invalid:
94 return false;
96 break;
98 case clang::analyze_format_string::ConversionSpecifier::PArg: { // "%P"
99 auto &precision = FS.getPrecision();
100 switch (precision.getHowSpecified()) {
101 case clang::analyze_format_string::OptionalAmount::NotSpecified: // "%P"
102 return false; // length must be supplied with pointer format specifier
103 case clang::analyze_format_string::OptionalAmount::Constant: // "%.16P"
104 ArgsData.back().Size = precision.getConstantAmount();
105 break;
106 case clang::analyze_format_string::OptionalAmount::Arg: // "%.*P"
107 ArgsData.back().Count = Args[precision.getArgIndex()];
108 break;
109 case clang::analyze_format_string::OptionalAmount::Invalid:
110 return false;
112 break;
114 default:
115 if (FS.getPrecision().hasDataArgument()) {
116 ArgsData.back().Precision = Args[FS.getPrecision().getArgIndex()];
118 break;
120 if (FS.getFieldWidth().hasDataArgument()) {
121 ArgsData.back().FieldWidth = Args[FS.getFieldWidth().getArgIndex()];
124 if (FS.isSensitive())
125 ArgsData.back().Flags |= OSLogBufferItem::IsSensitive;
126 else if (FS.isPrivate())
127 ArgsData.back().Flags |= OSLogBufferItem::IsPrivate;
128 else if (FS.isPublic())
129 ArgsData.back().Flags |= OSLogBufferItem::IsPublic;
131 ArgsData.back().MaskType = FS.getMaskType();
132 return true;
135 void computeLayout(ASTContext &Ctx, OSLogBufferLayout &Layout) const {
136 Layout.Items.clear();
137 for (auto &Data : ArgsData) {
138 if (!Data.MaskType.empty()) {
139 CharUnits Size = CharUnits::fromQuantity(8);
140 Layout.Items.emplace_back(OSLogBufferItem::MaskKind, nullptr,
141 Size, 0, Data.MaskType);
144 if (Data.FieldWidth) {
145 CharUnits Size = Ctx.getTypeSizeInChars((*Data.FieldWidth)->getType());
146 Layout.Items.emplace_back(OSLogBufferItem::ScalarKind, *Data.FieldWidth,
147 Size, 0);
149 if (Data.Precision) {
150 CharUnits Size = Ctx.getTypeSizeInChars((*Data.Precision)->getType());
151 Layout.Items.emplace_back(OSLogBufferItem::ScalarKind, *Data.Precision,
152 Size, 0);
154 if (Data.Count) {
155 // "%.*P" has an extra "count" that we insert before the argument.
156 CharUnits Size = Ctx.getTypeSizeInChars((*Data.Count)->getType());
157 Layout.Items.emplace_back(OSLogBufferItem::CountKind, *Data.Count, Size,
160 if (Data.Size)
161 Layout.Items.emplace_back(Ctx, CharUnits::fromQuantity(*Data.Size),
162 Data.Flags);
163 if (Data.Kind) {
164 CharUnits Size;
165 if (*Data.Kind == OSLogBufferItem::ErrnoKind)
166 Size = CharUnits::Zero();
167 else
168 Size = Ctx.getTypeSizeInChars(Data.E->getType());
169 Layout.Items.emplace_back(*Data.Kind, Data.E, Size, Data.Flags);
170 } else {
171 auto Size = Ctx.getTypeSizeInChars(Data.E->getType());
172 Layout.Items.emplace_back(OSLogBufferItem::ScalarKind, Data.E, Size,
173 Data.Flags);
178 } // end anonymous namespace
180 bool clang::analyze_os_log::computeOSLogBufferLayout(
181 ASTContext &Ctx, const CallExpr *E, OSLogBufferLayout &Layout) {
182 ArrayRef<const Expr *> Args(E->getArgs(), E->getArgs() + E->getNumArgs());
184 const Expr *StringArg;
185 ArrayRef<const Expr *> VarArgs;
186 switch (E->getBuiltinCallee()) {
187 case Builtin::BI__builtin_os_log_format_buffer_size:
188 assert(E->getNumArgs() >= 1 &&
189 "__builtin_os_log_format_buffer_size takes at least 1 argument");
190 StringArg = E->getArg(0);
191 VarArgs = Args.slice(1);
192 break;
193 case Builtin::BI__builtin_os_log_format:
194 assert(E->getNumArgs() >= 2 &&
195 "__builtin_os_log_format takes at least 2 arguments");
196 StringArg = E->getArg(1);
197 VarArgs = Args.slice(2);
198 break;
199 default:
200 llvm_unreachable("non-os_log builtin passed to computeOSLogBufferLayout");
203 const StringLiteral *Lit = cast<StringLiteral>(StringArg->IgnoreParenCasts());
204 assert(Lit && (Lit->isOrdinary() || Lit->isUTF8()));
205 StringRef Data = Lit->getString();
206 OSLogFormatStringHandler H(VarArgs);
207 ParsePrintfString(H, Data.begin(), Data.end(), Ctx.getLangOpts(),
208 Ctx.getTargetInfo(), /*isFreeBSDKPrintf*/ false);
210 H.computeLayout(Ctx, Layout);
211 return true;