Skip to content

Commit f301096

Browse files
committed
[analyzer] NFC: CallDescription: Implement describing C library functions.
When matching C standard library functions in the checker, it's easy to forget that they are often implemented as macros that are expanded to builtins. Such builtins would have a different name, so matching the callee identifier would fail, or may sometimes have more arguments than expected, so matching the exact number of arguments would fail, but this is fine as long as we have all the arguments that we need in their respective places. This patch adds a set of flags to the CallDescription class so that to handle various special matching rules, and adds the first flag into this set, which enables a more fuzzy matching for functions that may be implemented as compiler builtins. Differential Revision: https://reviews.llvm.org/D62556 llvm-svn: 364867
1 parent ec8e956 commit f301096

File tree

3 files changed

+45
-6
lines changed

3 files changed

+45
-6
lines changed

clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1044,6 +1044,14 @@ class ObjCMethodCall : public CallEvent {
10441044
}
10451045
};
10461046

1047+
enum CallDescriptionFlags : int {
1048+
/// Describes a C standard function that is sometimes implemented as a macro
1049+
/// that expands to a compiler builtin with some __builtin prefix.
1050+
/// The builtin may as well have a few extra arguments on top of the requested
1051+
/// number of arguments.
1052+
CDF_MaybeBuiltin = 1 << 0,
1053+
};
1054+
10471055
/// This class represents a description of a function call using the number of
10481056
/// arguments and the name of the function.
10491057
class CallDescription {
@@ -1055,6 +1063,7 @@ class CallDescription {
10551063
// e.g. "{a, b}" represent the qualified names, like "a::b".
10561064
std::vector<const char *> QualifiedName;
10571065
Optional<unsigned> RequiredArgs;
1066+
int Flags;
10581067

10591068
public:
10601069
/// Constructs a CallDescription object.
@@ -1067,9 +1076,15 @@ class CallDescription {
10671076
/// @param RequiredArgs The number of arguments that is expected to match a
10681077
/// call. Omit this parameter to match every occurrence of call with a given
10691078
/// name regardless the number of arguments.
1079+
CallDescription(int Flags, ArrayRef<const char *> QualifiedName,
1080+
Optional<unsigned> RequiredArgs = None)
1081+
: QualifiedName(QualifiedName), RequiredArgs(RequiredArgs),
1082+
Flags(Flags) {}
1083+
1084+
/// Construct a CallDescription with default flags.
10701085
CallDescription(ArrayRef<const char *> QualifiedName,
10711086
Optional<unsigned> RequiredArgs = None)
1072-
: QualifiedName(QualifiedName), RequiredArgs(RequiredArgs) {}
1087+
: CallDescription(0, QualifiedName, RequiredArgs) {}
10731088

10741089
/// Get the name of the function that this object matches.
10751090
StringRef getFunctionName() const { return QualifiedName.back(); }

clang/lib/StaticAnalyzer/Core/CallEvent.cpp

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -356,20 +356,32 @@ bool CallEvent::isCalled(const CallDescription &CD) const {
356356
// FIXME: Add ObjC Message support.
357357
if (getKind() == CE_ObjCMessage)
358358
return false;
359+
360+
const IdentifierInfo *II = getCalleeIdentifier();
361+
if (!II)
362+
return false;
363+
const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(getDecl());
364+
if (!FD)
365+
return false;
366+
367+
if (CD.Flags & CDF_MaybeBuiltin) {
368+
return CheckerContext::isCLibraryFunction(FD, CD.getFunctionName()) &&
369+
(!CD.RequiredArgs || CD.RequiredArgs <= getNumArgs());
370+
}
371+
359372
if (!CD.IsLookupDone) {
360373
CD.IsLookupDone = true;
361374
CD.II = &getState()->getStateManager().getContext().Idents.get(
362375
CD.getFunctionName());
363376
}
364-
const IdentifierInfo *II = getCalleeIdentifier();
365-
if (!II || II != CD.II)
377+
378+
if (II != CD.II)
366379
return false;
367380

368-
const Decl *D = getDecl();
369381
// If CallDescription provides prefix names, use them to improve matching
370382
// accuracy.
371-
if (CD.QualifiedName.size() > 1 && D) {
372-
const DeclContext *Ctx = D->getDeclContext();
383+
if (CD.QualifiedName.size() > 1 && FD) {
384+
const DeclContext *Ctx = FD->getDeclContext();
373385
// See if we'll be able to match them all.
374386
size_t NumUnmatched = CD.QualifiedName.size() - 1;
375387
for (; Ctx && isa<NamedDecl>(Ctx); Ctx = Ctx->getParent()) {

clang/unittests/StaticAnalyzer/CallDescriptionTest.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,18 @@ TEST(CallEvent, CallDescription) {
143143
{{{"bar", "foo"}}, false},
144144
{{"foo"}, true},
145145
}), "void foo(); struct bar { void foo(); }; void test() { foo(); }"));
146+
147+
// Test CDF_MaybeBuiltin - a flag that allows matching weird builtins.
148+
EXPECT_TRUE(tooling::runToolOnCode(
149+
new CallDescriptionAction({
150+
{{"memset", 3}, false},
151+
{{CDF_MaybeBuiltin, "memset", 3}, true}
152+
}),
153+
"void foo() {"
154+
" int x;"
155+
" __builtin___memset_chk(&x, 0, sizeof(x),"
156+
" __builtin_object_size(&x, 0));"
157+
"}"));
146158
}
147159

148160
} // namespace

0 commit comments

Comments
 (0)