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"
12 using namespace clang
;
14 using clang::analyze_os_log::OSLogBufferItem
;
15 using clang::analyze_os_log::OSLogBufferLayout
;
18 class OSLogFormatStringHandler
19 : public analyze_format_string::FormatStringHandler
{
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;
31 SmallVector
<ArgData
, 4> ArgsData
;
32 ArrayRef
<const Expr
*> Args
;
35 getKind(analyze_format_string::ConversionSpecifier::Kind 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
;
48 return OSLogBufferItem::ScalarKind
;
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
)
66 ArgsData
.emplace_back();
67 unsigned ArgIndex
= FS
.getArgIndex();
68 if (ArgIndex
< Args
.size())
69 ArgsData
.back().E
= Args
[ArgIndex
];
72 ArgsData
.back().Kind
= getKind(FS
.getConversionSpecifier().getKind());
73 if (ArgsData
.back().Kind
!= OSLogBufferItem::ErrnoKind
&&
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"
87 case clang::analyze_format_string::OptionalAmount::Constant
: // "%.16s"
88 ArgsData
.back().Size
= precision
.getConstantAmount();
90 case clang::analyze_format_string::OptionalAmount::Arg
: // "%.*s"
91 ArgsData
.back().Count
= Args
[precision
.getArgIndex()];
93 case clang::analyze_format_string::OptionalAmount::Invalid
:
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();
106 case clang::analyze_format_string::OptionalAmount::Arg
: // "%.*P"
107 ArgsData
.back().Count
= Args
[precision
.getArgIndex()];
109 case clang::analyze_format_string::OptionalAmount::Invalid
:
115 if (FS
.getPrecision().hasDataArgument()) {
116 ArgsData
.back().Precision
= Args
[FS
.getPrecision().getArgIndex()];
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();
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
,
149 if (Data
.Precision
) {
150 CharUnits Size
= Ctx
.getTypeSizeInChars((*Data
.Precision
)->getType());
151 Layout
.Items
.emplace_back(OSLogBufferItem::ScalarKind
, *Data
.Precision
,
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
,
161 Layout
.Items
.emplace_back(Ctx
, CharUnits::fromQuantity(*Data
.Size
),
165 if (*Data
.Kind
== OSLogBufferItem::ErrnoKind
)
166 Size
= CharUnits::Zero();
168 Size
= Ctx
.getTypeSizeInChars(Data
.E
->getType());
169 Layout
.Items
.emplace_back(*Data
.Kind
, Data
.E
, Size
, Data
.Flags
);
171 auto Size
= Ctx
.getTypeSizeInChars(Data
.E
->getType());
172 Layout
.Items
.emplace_back(OSLogBufferItem::ScalarKind
, Data
.E
, Size
,
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);
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);
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
);