]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/llvm/tools/clang/lib/Analysis/OSLog.cpp
MFC r345805: Unify SCSI_STATUS_BUSY retry handling with other cases.
[FreeBSD/FreeBSD.git] / contrib / llvm / tools / clang / lib / Analysis / OSLog.cpp
1 // TODO: header template
2
3 #include "clang/Analysis/Analyses/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/Analysis/Analyses/FormatString.h"
9 #include "clang/Basic/Builtins.h"
10 #include "llvm/ADT/SmallBitVector.h"
11
12 using namespace clang;
13
14 using clang::analyze_os_log::OSLogBufferItem;
15 using clang::analyze_os_log::OSLogBufferLayout;
16
17 namespace {
18 class OSLogFormatStringHandler
19     : public analyze_format_string::FormatStringHandler {
20 private:
21   struct ArgData {
22     const Expr *E = nullptr;
23     Optional<OSLogBufferItem::Kind> Kind;
24     Optional<unsigned> Size;
25     Optional<const Expr *> Count;
26     Optional<const Expr *> Precision;
27     Optional<const Expr *> FieldWidth;
28     unsigned char Flags = 0;
29   };
30   SmallVector<ArgData, 4> ArgsData;
31   ArrayRef<const Expr *> Args;
32
33   OSLogBufferItem::Kind
34   getKind(analyze_format_string::ConversionSpecifier::Kind K) {
35     switch (K) {
36     case clang::analyze_format_string::ConversionSpecifier::sArg: // "%s"
37       return OSLogBufferItem::StringKind;
38     case clang::analyze_format_string::ConversionSpecifier::SArg: // "%S"
39       return OSLogBufferItem::WideStringKind;
40     case clang::analyze_format_string::ConversionSpecifier::PArg: { // "%P"
41       return OSLogBufferItem::PointerKind;
42     case clang::analyze_format_string::ConversionSpecifier::ObjCObjArg: // "%@"
43       return OSLogBufferItem::ObjCObjKind;
44     case clang::analyze_format_string::ConversionSpecifier::PrintErrno: // "%m"
45       return OSLogBufferItem::ErrnoKind;
46     default:
47       return OSLogBufferItem::ScalarKind;
48     }
49     }
50   }
51
52 public:
53   OSLogFormatStringHandler(ArrayRef<const Expr *> Args) : Args(Args) {
54     ArgsData.reserve(Args.size());
55   }
56
57   virtual bool HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier &FS,
58                                      const char *StartSpecifier,
59                                      unsigned SpecifierLen) {
60     if (!FS.consumesDataArgument() &&
61         FS.getConversionSpecifier().getKind() !=
62             clang::analyze_format_string::ConversionSpecifier::PrintErrno)
63       return true;
64
65     ArgsData.emplace_back();
66     unsigned ArgIndex = FS.getArgIndex();
67     if (ArgIndex < Args.size())
68       ArgsData.back().E = Args[ArgIndex];
69
70     // First get the Kind
71     ArgsData.back().Kind = getKind(FS.getConversionSpecifier().getKind());
72     if (ArgsData.back().Kind != OSLogBufferItem::ErrnoKind &&
73         !ArgsData.back().E) {
74       // missing argument
75       ArgsData.pop_back();
76       return false;
77     }
78
79     switch (FS.getConversionSpecifier().getKind()) {
80     case clang::analyze_format_string::ConversionSpecifier::sArg:   // "%s"
81     case clang::analyze_format_string::ConversionSpecifier::SArg: { // "%S"
82       auto &precision = FS.getPrecision();
83       switch (precision.getHowSpecified()) {
84       case clang::analyze_format_string::OptionalAmount::NotSpecified: // "%s"
85         break;
86       case clang::analyze_format_string::OptionalAmount::Constant: // "%.16s"
87         ArgsData.back().Size = precision.getConstantAmount();
88         break;
89       case clang::analyze_format_string::OptionalAmount::Arg: // "%.*s"
90         ArgsData.back().Count = Args[precision.getArgIndex()];
91         break;
92       case clang::analyze_format_string::OptionalAmount::Invalid:
93         return false;
94       }
95       break;
96     }
97     case clang::analyze_format_string::ConversionSpecifier::PArg: { // "%P"
98       auto &precision = FS.getPrecision();
99       switch (precision.getHowSpecified()) {
100       case clang::analyze_format_string::OptionalAmount::NotSpecified: // "%P"
101         return false; // length must be supplied with pointer format specifier
102       case clang::analyze_format_string::OptionalAmount::Constant: // "%.16P"
103         ArgsData.back().Size = precision.getConstantAmount();
104         break;
105       case clang::analyze_format_string::OptionalAmount::Arg: // "%.*P"
106         ArgsData.back().Count = Args[precision.getArgIndex()];
107         break;
108       case clang::analyze_format_string::OptionalAmount::Invalid:
109         return false;
110       }
111       break;
112     }
113     default:
114       if (FS.getPrecision().hasDataArgument()) {
115         ArgsData.back().Precision = Args[FS.getPrecision().getArgIndex()];
116       }
117       break;
118     }
119     if (FS.getFieldWidth().hasDataArgument()) {
120       ArgsData.back().FieldWidth = Args[FS.getFieldWidth().getArgIndex()];
121     }
122
123     if (FS.isPrivate()) {
124       ArgsData.back().Flags |= OSLogBufferItem::IsPrivate;
125     }
126     if (FS.isPublic()) {
127       ArgsData.back().Flags |= OSLogBufferItem::IsPublic;
128     }
129     return true;
130   }
131
132   void computeLayout(ASTContext &Ctx, OSLogBufferLayout &Layout) const {
133     Layout.Items.clear();
134     for (auto &Data : ArgsData) {
135       if (Data.FieldWidth) {
136         CharUnits Size = Ctx.getTypeSizeInChars((*Data.FieldWidth)->getType());
137         Layout.Items.emplace_back(OSLogBufferItem::ScalarKind, *Data.FieldWidth,
138                                   Size, 0);
139       }
140       if (Data.Precision) {
141         CharUnits Size = Ctx.getTypeSizeInChars((*Data.Precision)->getType());
142         Layout.Items.emplace_back(OSLogBufferItem::ScalarKind, *Data.Precision,
143                                   Size, 0);
144       }
145       if (Data.Count) {
146         // "%.*P" has an extra "count" that we insert before the argument.
147         CharUnits Size = Ctx.getTypeSizeInChars((*Data.Count)->getType());
148         Layout.Items.emplace_back(OSLogBufferItem::CountKind, *Data.Count, Size,
149                                   0);
150       }
151       if (Data.Size)
152         Layout.Items.emplace_back(Ctx, CharUnits::fromQuantity(*Data.Size),
153                                   Data.Flags);
154       if (Data.Kind) {
155         CharUnits Size;
156         if (*Data.Kind == OSLogBufferItem::ErrnoKind)
157           Size = CharUnits::Zero();
158         else
159           Size = Ctx.getTypeSizeInChars(Data.E->getType());
160         Layout.Items.emplace_back(*Data.Kind, Data.E, Size, Data.Flags);
161       } else {
162         auto Size = Ctx.getTypeSizeInChars(Data.E->getType());
163         Layout.Items.emplace_back(OSLogBufferItem::ScalarKind, Data.E, Size,
164                                   Data.Flags);
165       }
166     }
167   }
168 };
169 } // end anonymous namespace
170
171 bool clang::analyze_os_log::computeOSLogBufferLayout(
172     ASTContext &Ctx, const CallExpr *E, OSLogBufferLayout &Layout) {
173   ArrayRef<const Expr *> Args(E->getArgs(), E->getArgs() + E->getNumArgs());
174
175   const Expr *StringArg;
176   ArrayRef<const Expr *> VarArgs;
177   switch (E->getBuiltinCallee()) {
178   case Builtin::BI__builtin_os_log_format_buffer_size:
179     assert(E->getNumArgs() >= 1 &&
180            "__builtin_os_log_format_buffer_size takes at least 1 argument");
181     StringArg = E->getArg(0);
182     VarArgs = Args.slice(1);
183     break;
184   case Builtin::BI__builtin_os_log_format:
185     assert(E->getNumArgs() >= 2 &&
186            "__builtin_os_log_format takes at least 2 arguments");
187     StringArg = E->getArg(1);
188     VarArgs = Args.slice(2);
189     break;
190   default:
191     llvm_unreachable("non-os_log builtin passed to computeOSLogBufferLayout");
192   }
193
194   const StringLiteral *Lit = cast<StringLiteral>(StringArg->IgnoreParenCasts());
195   assert(Lit && (Lit->isAscii() || Lit->isUTF8()));
196   StringRef Data = Lit->getString();
197   OSLogFormatStringHandler H(VarArgs);
198   ParsePrintfString(H, Data.begin(), Data.end(), Ctx.getLangOpts(),
199                     Ctx.getTargetInfo(), /*isFreeBSDKPrintf*/ false);
200
201   H.computeLayout(Ctx, Layout);
202   return true;
203 }