[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
This commit is contained in:
parent
ec8e95640f
commit
f301096f51
|
@ -1044,6 +1044,14 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum CallDescriptionFlags : int {
|
||||||
|
/// Describes a C standard function that is sometimes implemented as a macro
|
||||||
|
/// that expands to a compiler builtin with some __builtin prefix.
|
||||||
|
/// The builtin may as well have a few extra arguments on top of the requested
|
||||||
|
/// number of arguments.
|
||||||
|
CDF_MaybeBuiltin = 1 << 0,
|
||||||
|
};
|
||||||
|
|
||||||
/// This class represents a description of a function call using the number of
|
/// This class represents a description of a function call using the number of
|
||||||
/// arguments and the name of the function.
|
/// arguments and the name of the function.
|
||||||
class CallDescription {
|
class CallDescription {
|
||||||
|
@ -1055,6 +1063,7 @@ class CallDescription {
|
||||||
// e.g. "{a, b}" represent the qualified names, like "a::b".
|
// e.g. "{a, b}" represent the qualified names, like "a::b".
|
||||||
std::vector<const char *> QualifiedName;
|
std::vector<const char *> QualifiedName;
|
||||||
Optional<unsigned> RequiredArgs;
|
Optional<unsigned> RequiredArgs;
|
||||||
|
int Flags;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// Constructs a CallDescription object.
|
/// Constructs a CallDescription object.
|
||||||
|
@ -1067,9 +1076,15 @@ public:
|
||||||
/// @param RequiredArgs The number of arguments that is expected to match a
|
/// @param RequiredArgs The number of arguments that is expected to match a
|
||||||
/// call. Omit this parameter to match every occurrence of call with a given
|
/// call. Omit this parameter to match every occurrence of call with a given
|
||||||
/// name regardless the number of arguments.
|
/// name regardless the number of arguments.
|
||||||
|
CallDescription(int Flags, ArrayRef<const char *> QualifiedName,
|
||||||
|
Optional<unsigned> RequiredArgs = None)
|
||||||
|
: QualifiedName(QualifiedName), RequiredArgs(RequiredArgs),
|
||||||
|
Flags(Flags) {}
|
||||||
|
|
||||||
|
/// Construct a CallDescription with default flags.
|
||||||
CallDescription(ArrayRef<const char *> QualifiedName,
|
CallDescription(ArrayRef<const char *> QualifiedName,
|
||||||
Optional<unsigned> RequiredArgs = None)
|
Optional<unsigned> RequiredArgs = None)
|
||||||
: QualifiedName(QualifiedName), RequiredArgs(RequiredArgs) {}
|
: CallDescription(0, QualifiedName, RequiredArgs) {}
|
||||||
|
|
||||||
/// Get the name of the function that this object matches.
|
/// Get the name of the function that this object matches.
|
||||||
StringRef getFunctionName() const { return QualifiedName.back(); }
|
StringRef getFunctionName() const { return QualifiedName.back(); }
|
||||||
|
|
|
@ -356,20 +356,32 @@ bool CallEvent::isCalled(const CallDescription &CD) const {
|
||||||
// FIXME: Add ObjC Message support.
|
// FIXME: Add ObjC Message support.
|
||||||
if (getKind() == CE_ObjCMessage)
|
if (getKind() == CE_ObjCMessage)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
const IdentifierInfo *II = getCalleeIdentifier();
|
||||||
|
if (!II)
|
||||||
|
return false;
|
||||||
|
const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(getDecl());
|
||||||
|
if (!FD)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (CD.Flags & CDF_MaybeBuiltin) {
|
||||||
|
return CheckerContext::isCLibraryFunction(FD, CD.getFunctionName()) &&
|
||||||
|
(!CD.RequiredArgs || CD.RequiredArgs <= getNumArgs());
|
||||||
|
}
|
||||||
|
|
||||||
if (!CD.IsLookupDone) {
|
if (!CD.IsLookupDone) {
|
||||||
CD.IsLookupDone = true;
|
CD.IsLookupDone = true;
|
||||||
CD.II = &getState()->getStateManager().getContext().Idents.get(
|
CD.II = &getState()->getStateManager().getContext().Idents.get(
|
||||||
CD.getFunctionName());
|
CD.getFunctionName());
|
||||||
}
|
}
|
||||||
const IdentifierInfo *II = getCalleeIdentifier();
|
|
||||||
if (!II || II != CD.II)
|
if (II != CD.II)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const Decl *D = getDecl();
|
|
||||||
// If CallDescription provides prefix names, use them to improve matching
|
// If CallDescription provides prefix names, use them to improve matching
|
||||||
// accuracy.
|
// accuracy.
|
||||||
if (CD.QualifiedName.size() > 1 && D) {
|
if (CD.QualifiedName.size() > 1 && FD) {
|
||||||
const DeclContext *Ctx = D->getDeclContext();
|
const DeclContext *Ctx = FD->getDeclContext();
|
||||||
// See if we'll be able to match them all.
|
// See if we'll be able to match them all.
|
||||||
size_t NumUnmatched = CD.QualifiedName.size() - 1;
|
size_t NumUnmatched = CD.QualifiedName.size() - 1;
|
||||||
for (; Ctx && isa<NamedDecl>(Ctx); Ctx = Ctx->getParent()) {
|
for (; Ctx && isa<NamedDecl>(Ctx); Ctx = Ctx->getParent()) {
|
||||||
|
|
|
@ -143,6 +143,18 @@ TEST(CallEvent, CallDescription) {
|
||||||
{{{"bar", "foo"}}, false},
|
{{{"bar", "foo"}}, false},
|
||||||
{{"foo"}, true},
|
{{"foo"}, true},
|
||||||
}), "void foo(); struct bar { void foo(); }; void test() { foo(); }"));
|
}), "void foo(); struct bar { void foo(); }; void test() { foo(); }"));
|
||||||
|
|
||||||
|
// Test CDF_MaybeBuiltin - a flag that allows matching weird builtins.
|
||||||
|
EXPECT_TRUE(tooling::runToolOnCode(
|
||||||
|
new CallDescriptionAction({
|
||||||
|
{{"memset", 3}, false},
|
||||||
|
{{CDF_MaybeBuiltin, "memset", 3}, true}
|
||||||
|
}),
|
||||||
|
"void foo() {"
|
||||||
|
" int x;"
|
||||||
|
" __builtin___memset_chk(&x, 0, sizeof(x),"
|
||||||
|
" __builtin_object_size(&x, 0));"
|
||||||
|
"}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
Loading…
Reference in New Issue