blob: 93ad17cffb34d2dd11092e57b10b54a583ad968b [file] [log] [blame]
Artem Dergachevbba497f2016-10-24 09:41:38 +00001//=== StdLibraryFunctionsChecker.cpp - Model standard functions -*- C++ -*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This checker improves modeling of a few simple library functions.
11// It does not generate warnings.
12//
13// This checker provides a specification format - `FunctionSummaryTy' - and
14// contains descriptions of some library functions in this format. Each
15// specification contains a list of branches for splitting the program state
16// upon call, and range constraints on argument and return-value symbols that
17// are satisfied on each branch. This spec can be expanded to include more
18// items, like external effects of the function.
19//
20// The main difference between this approach and the body farms technique is
21// in more explicit control over how many branches are produced. For example,
22// consider standard C function `ispunct(int x)', which returns a non-zero value
23// iff `x' is a punctuation character, that is, when `x' is in range
24// ['!', '/'] [':', '@'] U ['[', '\`'] U ['{', '~'].
25// `FunctionSummaryTy' provides only two branches for this function. However,
26// any attempt to describe this range with if-statements in the body farm
27// would result in many more branches. Because each branch needs to be analyzed
28// independently, this significantly reduces performance. Additionally,
29// once we consider a branch on which `x' is in range, say, ['!', '/'],
30// we assume that such branch is an important separate path through the program,
31// which may lead to false positives because considering this particular path
32// was not consciously intended, and therefore it might have been unreachable.
33//
34// This checker uses eval::Call for modeling "pure" functions, for which
35// their `FunctionSummaryTy' is a precise model. This avoids unnecessary
36// invalidation passes. Conflicts with other checkers are unlikely because
37// if the function has no other effects, other checkers would probably never
38// want to improve upon the modeling done by this checker.
39//
40// Non-"pure" functions, for which only partial improvement over the default
41// behavior is expected, are modeled via check::PostCall, non-intrusively.
42//
43// The following standard C functions are currently supported:
44//
45// fgetc getline isdigit isupper
46// fread isalnum isgraph isxdigit
47// fwrite isalpha islower read
48// getc isascii isprint write
49// getchar isblank ispunct
50// getdelim iscntrl isspace
51//
52//===----------------------------------------------------------------------===//
53
54#include "ClangSACheckers.h"
55#include "clang/StaticAnalyzer/Core/Checker.h"
56#include "clang/StaticAnalyzer/Core/CheckerManager.h"
57#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
58#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
59
60using namespace clang;
61using namespace clang::ento;
62
63namespace {
64class StdLibraryFunctionsChecker : public Checker<check::PostCall, eval::Call> {
65 /// Below is a series of typedefs necessary to define function specs.
66 /// We avoid nesting types here because each additional qualifier
67 /// would need to be repeated in every function spec.
68 struct FunctionSummaryTy;
69
70 /// Specify how much the analyzer engine should entrust modeling this function
71 /// to us. If he doesn't, he performs additional invalidations.
72 enum InvalidationKindTy { NoEvalCall, EvalCallAsPure };
73
74 /// A pair of ValueRangeKindTy and IntRangeVectorTy would describe a range
75 /// imposed on a particular argument or return value symbol.
76 ///
77 /// Given a range, should the argument stay inside or outside this range?
78 /// The special `ComparesToArgument' value indicates that we should
79 /// impose a constraint that involves other argument or return value symbols.
80 enum ValueRangeKindTy { OutOfRange, WithinRange, ComparesToArgument };
81
Artem Dergachev47fec162016-11-02 19:35:20 +000082 // The universal integral type to use in value range descriptions.
83 // Unsigned to make sure overflows are well-defined.
84 typedef uint64_t RangeIntTy;
85
Artem Dergachevbba497f2016-10-24 09:41:38 +000086 /// Normally, describes a single range constraint, eg. {{0, 1}, {3, 4}} is
87 /// a non-negative integer, which less than 5 and not equal to 2. For
88 /// `ComparesToArgument', holds information about how exactly to compare to
89 /// the argument.
Artem Dergachev47fec162016-11-02 19:35:20 +000090 typedef std::vector<std::pair<RangeIntTy, RangeIntTy>> IntRangeVectorTy;
Artem Dergachevbba497f2016-10-24 09:41:38 +000091
92 /// A reference to an argument or return value by its number.
93 /// ArgNo in CallExpr and CallEvent is defined as Unsigned, but
94 /// obviously uint32_t should be enough for all practical purposes.
95 typedef uint32_t ArgNoTy;
96 static const ArgNoTy Ret = std::numeric_limits<ArgNoTy>::max();
97
98 /// Incapsulates a single range on a single symbol within a branch.
99 class ValueRange {
100 ArgNoTy ArgNo; // Argument to which we apply the range.
101 ValueRangeKindTy Kind; // Kind of range definition.
102 IntRangeVectorTy Args; // Polymorphic arguments.
103
104 public:
105 ValueRange(ArgNoTy ArgNo, ValueRangeKindTy Kind,
106 const IntRangeVectorTy &Args)
107 : ArgNo(ArgNo), Kind(Kind), Args(Args) {}
108
109 ArgNoTy getArgNo() const { return ArgNo; }
110 ValueRangeKindTy getKind() const { return Kind; }
111
112 BinaryOperator::Opcode getOpcode() const {
113 assert(Kind == ComparesToArgument);
114 assert(Args.size() == 1);
115 BinaryOperator::Opcode Op =
116 static_cast<BinaryOperator::Opcode>(Args[0].first);
117 assert(BinaryOperator::isComparisonOp(Op) &&
118 "Only comparison ops are supported for ComparesToArgument");
119 return Op;
120 }
121
122 ArgNoTy getOtherArgNo() const {
123 assert(Kind == ComparesToArgument);
124 assert(Args.size() == 1);
125 return static_cast<ArgNoTy>(Args[0].second);
126 }
127
128 const IntRangeVectorTy &getRanges() const {
129 assert(Kind != ComparesToArgument);
130 return Args;
131 }
132
133 // We avoid creating a virtual apply() method because
134 // it makes initializer lists harder to write.
135 private:
136 ProgramStateRef
137 applyAsOutOfRange(ProgramStateRef State, const CallEvent &Call,
138 const FunctionSummaryTy &Summary) const;
139 ProgramStateRef
140 applyAsWithinRange(ProgramStateRef State, const CallEvent &Call,
141 const FunctionSummaryTy &Summary) const;
142 ProgramStateRef
143 applyAsComparesToArgument(ProgramStateRef State, const CallEvent &Call,
144 const FunctionSummaryTy &Summary) const;
145
146 public:
147 ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call,
148 const FunctionSummaryTy &Summary) const {
149 switch (Kind) {
150 case OutOfRange:
151 return applyAsOutOfRange(State, Call, Summary);
152 case WithinRange:
153 return applyAsWithinRange(State, Call, Summary);
154 case ComparesToArgument:
155 return applyAsComparesToArgument(State, Call, Summary);
156 }
157 llvm_unreachable("Unknown ValueRange kind!");
158 }
159 };
160
161 /// The complete list of ranges that defines a single branch.
162 typedef std::vector<ValueRange> ValueRangeSet;
163
164 /// Includes information about function prototype (which is necessary to
165 /// ensure we're modeling the right function and casting values properly),
166 /// approach to invalidation, and a list of branches - essentially, a list
167 /// of list of ranges - essentially, a list of lists of lists of segments.
168 struct FunctionSummaryTy {
169 const std::vector<QualType> ArgTypes;
170 const QualType RetType;
171 const InvalidationKindTy InvalidationKind;
172 const std::vector<ValueRangeSet> Ranges;
173
174 private:
175 static void assertTypeSuitableForSummary(QualType T) {
176 assert(!T->isVoidType() &&
177 "We should have had no significant void types in the spec");
178 assert(T.isCanonical() &&
179 "We should only have canonical types in the spec");
180 // FIXME: lift this assert (but not the ones above!)
181 assert(T->isIntegralOrEnumerationType() &&
182 "We only support integral ranges in the spec");
183 }
184
185 public:
186 QualType getArgType(ArgNoTy ArgNo) const {
187 QualType T = (ArgNo == Ret) ? RetType : ArgTypes[ArgNo];
188 assertTypeSuitableForSummary(T);
189 return T;
190 }
191
192 /// Try our best to figure out if the call expression is the call of
193 /// *the* library function to which this specification applies.
194 bool matchesCall(const CallExpr *CE) const;
195 };
196
Artem Dergachev47fec162016-11-02 19:35:20 +0000197 // The same function (as in, function identifier) may have different
198 // summaries assigned to it, with different argument and return value types.
199 // We call these "variants" of the function. This can be useful for handling
200 // C++ function overloads, and also it can be used when the same function
201 // may have different definitions on different platforms.
202 typedef std::vector<FunctionSummaryTy> FunctionVariantsTy;
203
Artem Dergachevbba497f2016-10-24 09:41:38 +0000204 // The map of all functions supported by the checker. It is initialized
205 // lazily, and it doesn't change after initialization.
Artem Dergachev47fec162016-11-02 19:35:20 +0000206 typedef llvm::StringMap<FunctionVariantsTy> FunctionSummaryMapTy;
Artem Dergachevbba497f2016-10-24 09:41:38 +0000207 mutable FunctionSummaryMapTy FunctionSummaryMap;
208
209 // Auxiliary functions to support ArgNoTy within all structures
210 // in a unified manner.
211 static QualType getArgType(const FunctionSummaryTy &Summary, ArgNoTy ArgNo) {
212 return Summary.getArgType(ArgNo);
213 }
214 static QualType getArgType(const CallEvent &Call, ArgNoTy ArgNo) {
215 return ArgNo == Ret ? Call.getResultType().getCanonicalType()
216 : Call.getArgExpr(ArgNo)->getType().getCanonicalType();
217 }
218 static QualType getArgType(const CallExpr *CE, ArgNoTy ArgNo) {
219 return ArgNo == Ret ? CE->getType().getCanonicalType()
220 : CE->getArg(ArgNo)->getType().getCanonicalType();
221 }
222 static SVal getArgSVal(const CallEvent &Call, ArgNoTy ArgNo) {
223 return ArgNo == Ret ? Call.getReturnValue() : Call.getArgSVal(ArgNo);
224 }
225
226public:
227 void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
228 bool evalCall(const CallExpr *CE, CheckerContext &C) const;
229
230private:
231 Optional<FunctionSummaryTy> findFunctionSummary(const FunctionDecl *FD,
232 const CallExpr *CE,
233 CheckerContext &C) const;
234
235 void initFunctionSummaries(BasicValueFactory &BVF) const;
236};
237} // end of anonymous namespace
238
239ProgramStateRef StdLibraryFunctionsChecker::ValueRange::applyAsOutOfRange(
240 ProgramStateRef State, const CallEvent &Call,
241 const FunctionSummaryTy &Summary) const {
242
243 ProgramStateManager &Mgr = State->getStateManager();
244 SValBuilder &SVB = Mgr.getSValBuilder();
245 BasicValueFactory &BVF = SVB.getBasicValueFactory();
246 ConstraintManager &CM = Mgr.getConstraintManager();
247 QualType T = getArgType(Summary, getArgNo());
248 SVal V = getArgSVal(Call, getArgNo());
249
250 if (auto N = V.getAs<NonLoc>()) {
251 const IntRangeVectorTy &R = getRanges();
252 size_t E = R.size();
253 for (size_t I = 0; I != E; ++I) {
254 const llvm::APSInt &Min = BVF.getValue(R[I].first, T);
255 const llvm::APSInt &Max = BVF.getValue(R[I].second, T);
256 assert(Min <= Max);
Dominic Chen3f8c3fa72016-11-15 01:54:41 +0000257 State = CM.assumeInclusiveRange(State, *N, Min, Max, false);
Artem Dergachevbba497f2016-10-24 09:41:38 +0000258 if (!State)
259 break;
260 }
261 }
262
263 return State;
264}
265
266ProgramStateRef
267StdLibraryFunctionsChecker::ValueRange::applyAsWithinRange(
268 ProgramStateRef State, const CallEvent &Call,
269 const FunctionSummaryTy &Summary) const {
270
271 ProgramStateManager &Mgr = State->getStateManager();
272 SValBuilder &SVB = Mgr.getSValBuilder();
273 BasicValueFactory &BVF = SVB.getBasicValueFactory();
274 ConstraintManager &CM = Mgr.getConstraintManager();
275 QualType T = getArgType(Summary, getArgNo());
276 SVal V = getArgSVal(Call, getArgNo());
277
278 // "WithinRange R" is treated as "outside [T_MIN, T_MAX] \ R".
279 // We cut off [T_MIN, min(R) - 1] and [max(R) + 1, T_MAX] if necessary,
280 // and then cut away all holes in R one by one.
281 if (auto N = V.getAs<NonLoc>()) {
282 const IntRangeVectorTy &R = getRanges();
283 size_t E = R.size();
284
285 const llvm::APSInt &MinusInf = BVF.getMinValue(T);
286 const llvm::APSInt &PlusInf = BVF.getMaxValue(T);
287
Artem Dergachev26b0a9d2016-10-24 18:49:04 +0000288 const llvm::APSInt &Left = BVF.getValue(R[0].first - 1ULL, T);
Artem Dergachevbba497f2016-10-24 09:41:38 +0000289 if (Left != PlusInf) {
290 assert(MinusInf <= Left);
Dominic Chen3f8c3fa72016-11-15 01:54:41 +0000291 State = CM.assumeInclusiveRange(State, *N, MinusInf, Left, false);
Artem Dergachevbba497f2016-10-24 09:41:38 +0000292 if (!State)
293 return nullptr;
294 }
295
Artem Dergachev26b0a9d2016-10-24 18:49:04 +0000296 const llvm::APSInt &Right = BVF.getValue(R[E - 1].second + 1ULL, T);
Artem Dergachevbba497f2016-10-24 09:41:38 +0000297 if (Right != MinusInf) {
298 assert(Right <= PlusInf);
Dominic Chen3f8c3fa72016-11-15 01:54:41 +0000299 State = CM.assumeInclusiveRange(State, *N, Right, PlusInf, false);
Artem Dergachevbba497f2016-10-24 09:41:38 +0000300 if (!State)
301 return nullptr;
302 }
303
304 for (size_t I = 1; I != E; ++I) {
Artem Dergachev26b0a9d2016-10-24 18:49:04 +0000305 const llvm::APSInt &Min = BVF.getValue(R[I - 1].second + 1ULL, T);
306 const llvm::APSInt &Max = BVF.getValue(R[I].first - 1ULL, T);
Artem Dergachevbba497f2016-10-24 09:41:38 +0000307 assert(Min <= Max);
Dominic Chen3f8c3fa72016-11-15 01:54:41 +0000308 State = CM.assumeInclusiveRange(State, *N, Min, Max, false);
Artem Dergachevbba497f2016-10-24 09:41:38 +0000309 if (!State)
310 return nullptr;
311 }
312 }
313
314 return State;
315}
316
317ProgramStateRef
318StdLibraryFunctionsChecker::ValueRange::applyAsComparesToArgument(
319 ProgramStateRef State, const CallEvent &Call,
320 const FunctionSummaryTy &Summary) const {
321
322 ProgramStateManager &Mgr = State->getStateManager();
323 SValBuilder &SVB = Mgr.getSValBuilder();
324 QualType CondT = SVB.getConditionType();
325 QualType T = getArgType(Summary, getArgNo());
326 SVal V = getArgSVal(Call, getArgNo());
327
328 BinaryOperator::Opcode Op = getOpcode();
329 ArgNoTy OtherArg = getOtherArgNo();
330 SVal OtherV = getArgSVal(Call, OtherArg);
331 QualType OtherT = getArgType(Call, OtherArg);
332 // Note: we avoid integral promotion for comparison.
333 OtherV = SVB.evalCast(OtherV, T, OtherT);
334 if (auto CompV = SVB.evalBinOp(State, Op, V, OtherV, CondT)
335 .getAs<DefinedOrUnknownSVal>())
336 State = State->assume(*CompV, true);
337 return State;
338}
339
340void StdLibraryFunctionsChecker::checkPostCall(const CallEvent &Call,
341 CheckerContext &C) const {
342 const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
343 if (!FD)
344 return;
345
346 const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
347 if (!CE)
348 return;
349
350 Optional<FunctionSummaryTy> FoundSummary = findFunctionSummary(FD, CE, C);
351 if (!FoundSummary)
352 return;
353
354 // Now apply ranges.
355 const FunctionSummaryTy &Summary = *FoundSummary;
356 ProgramStateRef State = C.getState();
357
358 for (const auto &VRS: Summary.Ranges) {
359 ProgramStateRef NewState = State;
360 for (const auto &VR: VRS) {
361 NewState = VR.apply(NewState, Call, Summary);
362 if (!NewState)
363 break;
364 }
365
366 if (NewState && NewState != State)
367 C.addTransition(NewState);
368 }
369}
370
371bool StdLibraryFunctionsChecker::evalCall(const CallExpr *CE,
372 CheckerContext &C) const {
373 const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(CE->getCalleeDecl());
374 if (!FD)
375 return false;
376
377 Optional<FunctionSummaryTy> FoundSummary = findFunctionSummary(FD, CE, C);
378 if (!FoundSummary)
379 return false;
380
381 const FunctionSummaryTy &Summary = *FoundSummary;
382 switch (Summary.InvalidationKind) {
383 case EvalCallAsPure: {
384 ProgramStateRef State = C.getState();
385 const LocationContext *LC = C.getLocationContext();
386 SVal V = C.getSValBuilder().conjureSymbolVal(
387 CE, LC, CE->getType().getCanonicalType(), C.blockCount());
388 State = State->BindExpr(CE, LC, V);
389 C.addTransition(State);
390 return true;
391 }
392 case NoEvalCall:
393 // Summary tells us to avoid performing eval::Call. The function is possibly
394 // evaluated by another checker, or evaluated conservatively.
395 return false;
396 }
397 llvm_unreachable("Unknown invalidation kind!");
398}
399
400bool StdLibraryFunctionsChecker::FunctionSummaryTy::matchesCall(
401 const CallExpr *CE) const {
402 // Check number of arguments:
403 if (CE->getNumArgs() != ArgTypes.size())
404 return false;
405
406 // Check return type if relevant:
407 if (!RetType.isNull() && RetType != CE->getType().getCanonicalType())
408 return false;
409
410 // Check argument types when relevant:
411 for (size_t I = 0, E = ArgTypes.size(); I != E; ++I) {
412 QualType FormalT = ArgTypes[I];
413 // Null type marks irrelevant arguments.
414 if (FormalT.isNull())
415 continue;
416
417 assertTypeSuitableForSummary(FormalT);
418
419 QualType ActualT = StdLibraryFunctionsChecker::getArgType(CE, I);
420 assert(ActualT.isCanonical());
421 if (ActualT != FormalT)
422 return false;
423 }
424
425 return true;
426}
427
428Optional<StdLibraryFunctionsChecker::FunctionSummaryTy>
429StdLibraryFunctionsChecker::findFunctionSummary(const FunctionDecl *FD,
430 const CallExpr *CE,
431 CheckerContext &C) const {
432 // Note: we cannot always obtain FD from CE
433 // (eg. virtual call, or call by pointer).
434 assert(CE);
435
436 if (!FD)
437 return None;
438
439 SValBuilder &SVB = C.getSValBuilder();
440 BasicValueFactory &BVF = SVB.getBasicValueFactory();
441 initFunctionSummaries(BVF);
442
443 std::string Name = FD->getQualifiedNameAsString();
444 if (Name.empty() || !C.isCLibraryFunction(FD, Name))
445 return None;
446
447 auto FSMI = FunctionSummaryMap.find(Name);
448 if (FSMI == FunctionSummaryMap.end())
449 return None;
450
451 // Verify that function signature matches the spec in advance.
452 // Otherwise we might be modeling the wrong function.
453 // Strict checking is important because we will be conducting
454 // very integral-type-sensitive operations on arguments and
455 // return values.
Artem Dergachev47fec162016-11-02 19:35:20 +0000456 const FunctionVariantsTy &SpecVariants = FSMI->second;
457 for (const FunctionSummaryTy &Spec : SpecVariants)
458 if (Spec.matchesCall(CE))
459 return Spec;
Artem Dergachevbba497f2016-10-24 09:41:38 +0000460
Artem Dergachev47fec162016-11-02 19:35:20 +0000461 return None;
Artem Dergachevbba497f2016-10-24 09:41:38 +0000462}
463
464void StdLibraryFunctionsChecker::initFunctionSummaries(
465 BasicValueFactory &BVF) const {
466 if (!FunctionSummaryMap.empty())
467 return;
468
469 ASTContext &ACtx = BVF.getContext();
470
471 // These types are useful for writing specifications quickly,
472 // New specifications should probably introduce more types.
Artem Dergachev47fec162016-11-02 19:35:20 +0000473 // Some types are hard to obtain from the AST, eg. "ssize_t".
474 // In such cases it should be possible to provide multiple variants
475 // of function summary for common cases (eg. ssize_t could be int or long
476 // or long long, so three summary variants would be enough).
477 // Of course, function variants are also useful for C++ overloads.
Artem Dergachevbba497f2016-10-24 09:41:38 +0000478 QualType Irrelevant; // A placeholder, whenever we do not care about the type.
479 QualType IntTy = ACtx.IntTy;
Artem Dergachev47fec162016-11-02 19:35:20 +0000480 QualType LongTy = ACtx.LongTy;
481 QualType LongLongTy = ACtx.LongLongTy;
Artem Dergachevbba497f2016-10-24 09:41:38 +0000482 QualType SizeTy = ACtx.getSizeType();
Artem Dergachevbba497f2016-10-24 09:41:38 +0000483
Artem Dergachev47fec162016-11-02 19:35:20 +0000484 RangeIntTy IntMax = BVF.getMaxValue(IntTy).getLimitedValue();
485 RangeIntTy LongMax = BVF.getMaxValue(LongTy).getLimitedValue();
486 RangeIntTy LongLongMax = BVF.getMaxValue(LongLongTy).getLimitedValue();
Artem Dergachevbba497f2016-10-24 09:41:38 +0000487
488 // We are finally ready to define specifications for all supported functions.
489 //
490 // The signature needs to have the correct number of arguments.
491 // However, we insert `Irrelevant' when the type is insignificant.
492 //
493 // Argument ranges should always cover all variants. If return value
494 // is completely unknown, omit it from the respective range set.
495 //
496 // All types in the spec need to be canonical.
497 //
498 // Every item in the list of range sets represents a particular
499 // execution path the analyzer would need to explore once
500 // the call is modeled - a new program state is constructed
501 // for every range set, and each range line in the range set
502 // corresponds to a specific constraint within this state.
503 //
504 // Upon comparing to another argument, the other argument is casted
505 // to the current argument's type. This avoids proper promotion but
506 // seems useful. For example, read() receives size_t argument,
507 // and its return value, which is of type ssize_t, cannot be greater
508 // than this argument. If we made a promotion, and the size argument
509 // is equal to, say, 10, then we'd impose a range of [0, 10] on the
510 // return value, however the correct range is [-1, 10].
511 //
512 // Please update the list of functions in the header after editing!
513 //
514 // The format is as follows:
515 //
516 //{ "function name",
517 // { spec:
518 // { argument types list, ... },
519 // return type, purity, { range set list:
520 // { range list:
521 // { argument index, within or out of, {{from, to}, ...} },
522 // { argument index, compares to argument, {{how, which}} },
523 // ...
524 // }
525 // }
526 // }
527 //}
528
Artem Dergachev47fec162016-11-02 19:35:20 +0000529#define SUMMARY_WITH_VARIANTS(identifier) {#identifier, {
530#define END_SUMMARY_WITH_VARIANTS }},
531#define VARIANT(argument_types, return_type, invalidation_approach) \
532 { argument_types, return_type, invalidation_approach, {
533#define END_VARIANT } },
Artem Dergachevbba497f2016-10-24 09:41:38 +0000534#define SUMMARY(identifier, argument_types, return_type, \
535 invalidation_approach) \
Artem Dergachev47fec162016-11-02 19:35:20 +0000536 { #identifier, { { argument_types, return_type, invalidation_approach, {
537#define END_SUMMARY } } } },
Artem Dergachevbba497f2016-10-24 09:41:38 +0000538#define ARGUMENT_TYPES(...) { __VA_ARGS__ }
539#define RETURN_TYPE(x) x
540#define INVALIDATION_APPROACH(x) x
541#define CASE {
542#define END_CASE },
543#define ARGUMENT_CONDITION(argument_number, condition_kind) \
Artem Dergachev47fec162016-11-02 19:35:20 +0000544 { argument_number, condition_kind, {
Artem Dergachevbba497f2016-10-24 09:41:38 +0000545#define END_ARGUMENT_CONDITION }},
546#define RETURN_VALUE_CONDITION(condition_kind) \
547 { Ret, condition_kind, {
548#define END_RETURN_VALUE_CONDITION }},
549#define ARG_NO(x) x##U
550#define RANGE(x, y) { x, y },
551#define SINGLE_VALUE(x) RANGE(x, x)
552#define IS_LESS_THAN(arg) { BO_LE, arg }
553
554 FunctionSummaryMap = {
555 // The isascii() family of functions.
556 SUMMARY(isalnum, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
557 INVALIDATION_APPROACH(EvalCallAsPure))
558 CASE // Boils down to isupper() or islower() or isdigit()
559 ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
560 RANGE('0', '9')
561 RANGE('A', 'Z')
562 RANGE('a', 'z')
563 END_ARGUMENT_CONDITION
564 RETURN_VALUE_CONDITION(OutOfRange)
565 SINGLE_VALUE(0)
566 END_RETURN_VALUE_CONDITION
567 END_CASE
568 CASE // The locale-specific range.
569 ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
570 RANGE(128, 255)
571 END_ARGUMENT_CONDITION
572 // No post-condition. We are completely unaware of
573 // locale-specific return values.
574 END_CASE
575 CASE
576 ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
577 RANGE('0', '9')
578 RANGE('A', 'Z')
579 RANGE('a', 'z')
580 RANGE(128, 255)
581 END_ARGUMENT_CONDITION
582 RETURN_VALUE_CONDITION(WithinRange)
583 SINGLE_VALUE(0)
584 END_RETURN_VALUE_CONDITION
585 END_CASE
586 END_SUMMARY
587 SUMMARY(isalpha, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
588 INVALIDATION_APPROACH(EvalCallAsPure))
589 CASE // isupper() or islower(). Note that 'Z' is less than 'a'.
590 ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
591 RANGE('A', 'Z')
592 RANGE('a', 'z')
593 END_ARGUMENT_CONDITION
594 RETURN_VALUE_CONDITION(OutOfRange)
595 SINGLE_VALUE(0)
596 END_RETURN_VALUE_CONDITION
597 END_CASE
598 CASE // The locale-specific range.
599 ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
600 RANGE(128, 255)
601 END_ARGUMENT_CONDITION
602 END_CASE
603 CASE // Other.
604 ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
605 RANGE('A', 'Z')
606 RANGE('a', 'z')
607 RANGE(128, 255)
608 END_ARGUMENT_CONDITION
609 RETURN_VALUE_CONDITION(WithinRange)
610 SINGLE_VALUE(0)
611 END_RETURN_VALUE_CONDITION
612 END_CASE
613 END_SUMMARY
614 SUMMARY(isascii, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
615 INVALIDATION_APPROACH(EvalCallAsPure))
616 CASE // Is ASCII.
617 ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
618 RANGE(0, 127)
619 END_ARGUMENT_CONDITION
620 RETURN_VALUE_CONDITION(OutOfRange)
621 SINGLE_VALUE(0)
622 END_RETURN_VALUE_CONDITION
623 END_CASE
624 CASE
625 ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
626 RANGE(0, 127)
627 END_ARGUMENT_CONDITION
628 RETURN_VALUE_CONDITION(WithinRange)
629 SINGLE_VALUE(0)
630 END_RETURN_VALUE_CONDITION
631 END_CASE
632 END_SUMMARY
633 SUMMARY(isblank, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
634 INVALIDATION_APPROACH(EvalCallAsPure))
635 CASE
636 ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
637 SINGLE_VALUE('\t')
638 SINGLE_VALUE(' ')
639 END_ARGUMENT_CONDITION
640 RETURN_VALUE_CONDITION(OutOfRange)
641 SINGLE_VALUE(0)
642 END_RETURN_VALUE_CONDITION
643 END_CASE
644 CASE
645 ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
646 SINGLE_VALUE('\t')
647 SINGLE_VALUE(' ')
648 END_ARGUMENT_CONDITION
649 RETURN_VALUE_CONDITION(WithinRange)
650 SINGLE_VALUE(0)
651 END_RETURN_VALUE_CONDITION
652 END_CASE
653 END_SUMMARY
654 SUMMARY(iscntrl, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
655 INVALIDATION_APPROACH(EvalCallAsPure))
656 CASE // 0..31 or 127
657 ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
658 RANGE(0, 32)
659 SINGLE_VALUE(127)
660 END_ARGUMENT_CONDITION
661 RETURN_VALUE_CONDITION(OutOfRange)
662 SINGLE_VALUE(0)
663 END_RETURN_VALUE_CONDITION
664 END_CASE
665 CASE
666 ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
667 RANGE(0, 32)
668 SINGLE_VALUE(127)
669 END_ARGUMENT_CONDITION
670 RETURN_VALUE_CONDITION(WithinRange)
671 SINGLE_VALUE(0)
672 END_RETURN_VALUE_CONDITION
673 END_CASE
674 END_SUMMARY
675 SUMMARY(isdigit, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
676 INVALIDATION_APPROACH(EvalCallAsPure))
677 CASE // Is a digit.
678 ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
679 RANGE('0', '9')
680 END_ARGUMENT_CONDITION
681 RETURN_VALUE_CONDITION(OutOfRange)
682 SINGLE_VALUE(0)
683 END_RETURN_VALUE_CONDITION
684 END_CASE
685 CASE
686 ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
687 RANGE('0', '9')
688 END_ARGUMENT_CONDITION
689 RETURN_VALUE_CONDITION(WithinRange)
690 SINGLE_VALUE(0)
691 END_RETURN_VALUE_CONDITION
692 END_CASE
693 END_SUMMARY
694 SUMMARY(isgraph, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
695 INVALIDATION_APPROACH(EvalCallAsPure))
696 CASE
697 ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
698 RANGE(33, 126)
699 END_ARGUMENT_CONDITION
700 RETURN_VALUE_CONDITION(OutOfRange)
701 SINGLE_VALUE(0)
702 END_RETURN_VALUE_CONDITION
703 END_CASE
704 CASE
705 ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
706 RANGE(33, 126)
707 END_ARGUMENT_CONDITION
708 RETURN_VALUE_CONDITION(WithinRange)
709 SINGLE_VALUE(0)
710 END_RETURN_VALUE_CONDITION
711 END_CASE
712 END_SUMMARY
713 SUMMARY(islower, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
714 INVALIDATION_APPROACH(EvalCallAsPure))
715 CASE // Is certainly lowercase.
716 ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
717 RANGE('a', 'z')
718 END_ARGUMENT_CONDITION
719 RETURN_VALUE_CONDITION(OutOfRange)
720 SINGLE_VALUE(0)
721 END_RETURN_VALUE_CONDITION
722 END_CASE
723 CASE // Is ascii but not lowercase.
724 ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
725 RANGE(0, 127)
726 END_ARGUMENT_CONDITION
727 ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
728 RANGE('a', 'z')
729 END_ARGUMENT_CONDITION
730 RETURN_VALUE_CONDITION(WithinRange)
731 SINGLE_VALUE(0)
732 END_RETURN_VALUE_CONDITION
733 END_CASE
734 CASE // The locale-specific range.
735 ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
736 RANGE(128, 255)
737 END_ARGUMENT_CONDITION
738 END_CASE
739 CASE // Is not an unsigned char.
740 ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
741 RANGE(0, 255)
742 END_ARGUMENT_CONDITION
743 RETURN_VALUE_CONDITION(WithinRange)
744 SINGLE_VALUE(0)
745 END_RETURN_VALUE_CONDITION
746 END_CASE
747 END_SUMMARY
748 SUMMARY(isprint, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
749 INVALIDATION_APPROACH(EvalCallAsPure))
750 CASE
751 ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
752 RANGE(32, 126)
753 END_ARGUMENT_CONDITION
754 RETURN_VALUE_CONDITION(OutOfRange)
755 SINGLE_VALUE(0)
756 END_RETURN_VALUE_CONDITION
757 END_CASE
758 CASE
759 ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
760 RANGE(32, 126)
761 END_ARGUMENT_CONDITION
762 RETURN_VALUE_CONDITION(WithinRange)
763 SINGLE_VALUE(0)
764 END_RETURN_VALUE_CONDITION
765 END_CASE
766 END_SUMMARY
767 SUMMARY(ispunct, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
768 INVALIDATION_APPROACH(EvalCallAsPure))
769 CASE
770 ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
771 RANGE('!', '/')
772 RANGE(':', '@')
773 RANGE('[', '`')
774 RANGE('{', '~')
775 END_ARGUMENT_CONDITION
776 RETURN_VALUE_CONDITION(OutOfRange)
777 SINGLE_VALUE(0)
778 END_RETURN_VALUE_CONDITION
779 END_CASE
780 CASE
781 ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
782 RANGE('!', '/')
783 RANGE(':', '@')
784 RANGE('[', '`')
785 RANGE('{', '~')
786 END_ARGUMENT_CONDITION
787 RETURN_VALUE_CONDITION(WithinRange)
788 SINGLE_VALUE(0)
789 END_RETURN_VALUE_CONDITION
790 END_CASE
791 END_SUMMARY
792 SUMMARY(isspace, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
793 INVALIDATION_APPROACH(EvalCallAsPure))
794 CASE // Space, '\f', '\n', '\r', '\t', '\v'.
795 ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
796 RANGE(9, 13)
797 SINGLE_VALUE(' ')
798 END_ARGUMENT_CONDITION
799 RETURN_VALUE_CONDITION(OutOfRange)
800 SINGLE_VALUE(0)
801 END_RETURN_VALUE_CONDITION
802 END_CASE
803 CASE // The locale-specific range.
804 ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
805 RANGE(128, 255)
806 END_ARGUMENT_CONDITION
807 END_CASE
808 CASE
809 ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
810 RANGE(9, 13)
811 SINGLE_VALUE(' ')
812 RANGE(128, 255)
813 END_ARGUMENT_CONDITION
814 RETURN_VALUE_CONDITION(WithinRange)
815 SINGLE_VALUE(0)
816 END_RETURN_VALUE_CONDITION
817 END_CASE
818 END_SUMMARY
819 SUMMARY(isupper, ARGUMENT_TYPES(IntTy), RETURN_TYPE (IntTy),
820 INVALIDATION_APPROACH(EvalCallAsPure))
821 CASE // Is certainly uppercase.
822 ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
823 RANGE('A', 'Z')
824 END_ARGUMENT_CONDITION
825 RETURN_VALUE_CONDITION(OutOfRange)
826 SINGLE_VALUE(0)
827 END_RETURN_VALUE_CONDITION
828 END_CASE
829 CASE // The locale-specific range.
830 ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
831 RANGE(128, 255)
832 END_ARGUMENT_CONDITION
833 END_CASE
834 CASE // Other.
835 ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
836 RANGE('A', 'Z') RANGE(128, 255)
837 END_ARGUMENT_CONDITION
838 RETURN_VALUE_CONDITION(WithinRange)
839 SINGLE_VALUE(0)
840 END_RETURN_VALUE_CONDITION
841 END_CASE
842 END_SUMMARY
843 SUMMARY(isxdigit, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
844 INVALIDATION_APPROACH(EvalCallAsPure))
845 CASE
846 ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
847 RANGE('0', '9')
848 RANGE('A', 'F')
849 RANGE('a', 'f')
850 END_ARGUMENT_CONDITION
851 RETURN_VALUE_CONDITION(OutOfRange)
852 SINGLE_VALUE(0)
853 END_RETURN_VALUE_CONDITION
854 END_CASE
855 CASE
856 ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
857 RANGE('0', '9')
858 RANGE('A', 'F')
859 RANGE('a', 'f')
860 END_ARGUMENT_CONDITION
861 RETURN_VALUE_CONDITION(WithinRange)
862 SINGLE_VALUE(0)
863 END_RETURN_VALUE_CONDITION
864 END_CASE
865 END_SUMMARY
866
867 // The getc() family of functions that returns either a char or an EOF.
868 SUMMARY(getc, ARGUMENT_TYPES(Irrelevant), RETURN_TYPE(IntTy),
869 INVALIDATION_APPROACH(NoEvalCall))
870 CASE // FIXME: EOF is assumed to be defined as -1.
871 RETURN_VALUE_CONDITION(WithinRange)
872 RANGE(-1, 255)
873 END_RETURN_VALUE_CONDITION
874 END_CASE
875 END_SUMMARY
876 SUMMARY(fgetc, ARGUMENT_TYPES(Irrelevant), RETURN_TYPE(IntTy),
877 INVALIDATION_APPROACH(NoEvalCall))
878 CASE // FIXME: EOF is assumed to be defined as -1.
879 RETURN_VALUE_CONDITION(WithinRange)
880 RANGE(-1, 255)
881 END_RETURN_VALUE_CONDITION
882 END_CASE
883 END_SUMMARY
884 SUMMARY(getchar, ARGUMENT_TYPES(), RETURN_TYPE(IntTy),
885 INVALIDATION_APPROACH(NoEvalCall))
886 CASE // FIXME: EOF is assumed to be defined as -1.
887 RETURN_VALUE_CONDITION(WithinRange)
888 RANGE(-1, 255)
889 END_RETURN_VALUE_CONDITION
890 END_CASE
891 END_SUMMARY
892
893 // read()-like functions that never return more than buffer size.
Artem Dergachev47fec162016-11-02 19:35:20 +0000894 // We are not sure how ssize_t is defined on every platform, so we provide
895 // three variants that should cover common cases.
896 SUMMARY_WITH_VARIANTS(read)
897 VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy),
898 RETURN_TYPE(IntTy), INVALIDATION_APPROACH(NoEvalCall))
899 CASE
900 RETURN_VALUE_CONDITION(ComparesToArgument)
901 IS_LESS_THAN(ARG_NO(2))
902 END_RETURN_VALUE_CONDITION
903 RETURN_VALUE_CONDITION(WithinRange)
904 RANGE(-1, IntMax)
905 END_RETURN_VALUE_CONDITION
906 END_CASE
907 END_VARIANT
908 VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy),
909 RETURN_TYPE(LongTy), INVALIDATION_APPROACH(NoEvalCall))
910 CASE
911 RETURN_VALUE_CONDITION(ComparesToArgument)
912 IS_LESS_THAN(ARG_NO(2))
913 END_RETURN_VALUE_CONDITION
914 RETURN_VALUE_CONDITION(WithinRange)
915 RANGE(-1, LongMax)
916 END_RETURN_VALUE_CONDITION
917 END_CASE
918 END_VARIANT
919 VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy),
920 RETURN_TYPE(LongLongTy), INVALIDATION_APPROACH(NoEvalCall))
921 CASE
922 RETURN_VALUE_CONDITION(ComparesToArgument)
923 IS_LESS_THAN(ARG_NO(2))
924 END_RETURN_VALUE_CONDITION
925 RETURN_VALUE_CONDITION(WithinRange)
926 RANGE(-1, LongLongMax)
927 END_RETURN_VALUE_CONDITION
928 END_CASE
929 END_VARIANT
930 END_SUMMARY_WITH_VARIANTS
931 SUMMARY_WITH_VARIANTS(write)
932 // Again, due to elusive nature of ssize_t, we have duplicate
933 // our summaries to cover different variants.
934 VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy),
935 RETURN_TYPE(IntTy), INVALIDATION_APPROACH(NoEvalCall))
936 CASE
937 RETURN_VALUE_CONDITION(ComparesToArgument)
938 IS_LESS_THAN(ARG_NO(2))
939 END_RETURN_VALUE_CONDITION
940 RETURN_VALUE_CONDITION(WithinRange)
941 RANGE(-1, IntMax)
942 END_RETURN_VALUE_CONDITION
943 END_CASE
944 END_VARIANT
945 VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy),
946 RETURN_TYPE(LongTy), INVALIDATION_APPROACH(NoEvalCall))
947 CASE
948 RETURN_VALUE_CONDITION(ComparesToArgument)
949 IS_LESS_THAN(ARG_NO(2))
950 END_RETURN_VALUE_CONDITION
951 RETURN_VALUE_CONDITION(WithinRange)
952 RANGE(-1, LongMax)
953 END_RETURN_VALUE_CONDITION
954 END_CASE
955 END_VARIANT
956 VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy),
957 RETURN_TYPE(LongLongTy), INVALIDATION_APPROACH(NoEvalCall))
958 CASE
959 RETURN_VALUE_CONDITION(ComparesToArgument)
960 IS_LESS_THAN(ARG_NO(2))
961 END_RETURN_VALUE_CONDITION
962 RETURN_VALUE_CONDITION(WithinRange)
963 RANGE(-1, LongLongMax)
964 END_RETURN_VALUE_CONDITION
965 END_CASE
966 END_VARIANT
967 END_SUMMARY_WITH_VARIANTS
Artem Dergachevbba497f2016-10-24 09:41:38 +0000968 SUMMARY(fread,
969 ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy, Irrelevant),
970 RETURN_TYPE(SizeTy), INVALIDATION_APPROACH(NoEvalCall))
971 CASE
972 RETURN_VALUE_CONDITION(ComparesToArgument)
973 IS_LESS_THAN(ARG_NO(2))
974 END_RETURN_VALUE_CONDITION
975 END_CASE
976 END_SUMMARY
977 SUMMARY(fwrite,
978 ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy, Irrelevant),
979 RETURN_TYPE(SizeTy), INVALIDATION_APPROACH(NoEvalCall))
980 CASE
981 RETURN_VALUE_CONDITION(ComparesToArgument)
982 IS_LESS_THAN(ARG_NO(2))
983 END_RETURN_VALUE_CONDITION
984 END_CASE
985 END_SUMMARY
986
987 // getline()-like functions either fail or read at least the delimiter.
Artem Dergachev47fec162016-11-02 19:35:20 +0000988 SUMMARY_WITH_VARIANTS(getline)
989 VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant),
990 RETURN_TYPE(IntTy), INVALIDATION_APPROACH(NoEvalCall))
991 CASE
992 RETURN_VALUE_CONDITION(WithinRange)
993 SINGLE_VALUE(-1)
994 RANGE(1, IntMax)
995 END_RETURN_VALUE_CONDITION
996 END_CASE
997 END_VARIANT
998 VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant),
999 RETURN_TYPE(LongTy), INVALIDATION_APPROACH(NoEvalCall))
1000 CASE
1001 RETURN_VALUE_CONDITION(WithinRange)
1002 SINGLE_VALUE(-1)
1003 RANGE(1, LongMax)
1004 END_RETURN_VALUE_CONDITION
1005 END_CASE
1006 END_VARIANT
1007 VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant),
1008 RETURN_TYPE(LongLongTy), INVALIDATION_APPROACH(NoEvalCall))
1009 CASE
1010 RETURN_VALUE_CONDITION(WithinRange)
1011 SINGLE_VALUE(-1)
1012 RANGE(1, LongLongMax)
1013 END_RETURN_VALUE_CONDITION
1014 END_CASE
1015 END_VARIANT
1016 END_SUMMARY_WITH_VARIANTS
1017 SUMMARY_WITH_VARIANTS(getdelim)
1018 VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant, Irrelevant),
1019 RETURN_TYPE(IntTy), INVALIDATION_APPROACH(NoEvalCall))
1020 CASE
1021 RETURN_VALUE_CONDITION(WithinRange)
1022 SINGLE_VALUE(-1)
1023 RANGE(1, IntMax)
1024 END_RETURN_VALUE_CONDITION
1025 END_CASE
1026 END_VARIANT
1027 VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant, Irrelevant),
1028 RETURN_TYPE(LongTy), INVALIDATION_APPROACH(NoEvalCall))
1029 CASE
1030 RETURN_VALUE_CONDITION(WithinRange)
1031 SINGLE_VALUE(-1)
1032 RANGE(1, LongMax)
1033 END_RETURN_VALUE_CONDITION
1034 END_CASE
1035 END_VARIANT
1036 VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant, Irrelevant),
1037 RETURN_TYPE(LongLongTy), INVALIDATION_APPROACH(NoEvalCall))
1038 CASE
1039 RETURN_VALUE_CONDITION(WithinRange)
1040 SINGLE_VALUE(-1)
1041 RANGE(1, LongLongMax)
1042 END_RETURN_VALUE_CONDITION
1043 END_CASE
1044 END_VARIANT
1045 END_SUMMARY_WITH_VARIANTS
Artem Dergachevbba497f2016-10-24 09:41:38 +00001046 };
1047}
1048
1049void ento::registerStdCLibraryFunctionsChecker(CheckerManager &mgr) {
1050 // If this checker grows large enough to support C++, Objective-C, or other
1051 // standard libraries, we could use multiple register...Checker() functions,
1052 // which would register various checkers with the help of the same Checker
1053 // class, turning on different function summaries.
1054 mgr.registerChecker<StdLibraryFunctionsChecker>();
1055}