diff --git a/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp b/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp --- a/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp +++ b/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp @@ -55,8 +55,29 @@ if (Name.empty()) return true; StringRef BName = FD->getASTContext().BuiltinInfo.getName(BId); - if (BName.contains(Name)) - return true; + size_t start = BName.find(Name); + if (start != StringRef::npos) { + // Accept exact match. + if (BName.size() == Name.size()) + return true; + + // v-- match starts here + // ...xxxxx... + // _xxxxx_ + // ^ ^ lookbehind and lookahead characters + + const auto MatchPredecessor = [=]() -> bool { + return start <= 0 || !llvm::isAlpha(BName[start - 1]); + }; + const auto MatchSuccessor = [=]() -> bool { + std::size_t LookbehindPlace = start + Name.size(); + return LookbehindPlace >= BName.size() || + !llvm::isAlpha(BName[LookbehindPlace]); + }; + + if (MatchPredecessor() && MatchSuccessor()) + return true; + } } const IdentifierInfo *II = FD->getIdentifier(); diff --git a/clang/unittests/StaticAnalyzer/CallDescriptionTest.cpp b/clang/unittests/StaticAnalyzer/CallDescriptionTest.cpp --- a/clang/unittests/StaticAnalyzer/CallDescriptionTest.cpp +++ b/clang/unittests/StaticAnalyzer/CallDescriptionTest.cpp @@ -487,6 +487,60 @@ " __builtin___memset_chk(&x, 0, sizeof(x)," " __builtin_object_size(&x, 0));" "}")); + + { + SCOPED_TRACE("multiple similar builtins"); + EXPECT_TRUE(tooling::runToolOnCode( + std::unique_ptr(new CallDescriptionAction<>( + {{{CDF_MaybeBuiltin, "memcpy", 3}, false}, + {{CDF_MaybeBuiltin, "wmemcpy", 3}, true}})), + R"(void foo(wchar_t *x, wchar_t *y) { + __builtin_wmemcpy(x, y, sizeof(wchar_t)); + })")); + } + { + SCOPED_TRACE("multiple similar builtins reversed order"); + EXPECT_TRUE(tooling::runToolOnCode( + std::unique_ptr(new CallDescriptionAction<>( + {{{CDF_MaybeBuiltin, "wmemcpy", 3}, true}, + {{CDF_MaybeBuiltin, "memcpy", 3}, false}})), + R"(void foo(wchar_t *x, wchar_t *y) { + __builtin_wmemcpy(x, y, sizeof(wchar_t)); + })")); + } + { + SCOPED_TRACE("lookbehind and lookahead mismatches"); + EXPECT_TRUE(tooling::runToolOnCode( + std::unique_ptr( + new CallDescriptionAction<>({{{CDF_MaybeBuiltin, "func"}, false}})), + R"( + void funcXXX(); + void XXXfunc(); + void XXXfuncXXX(); + void test() { + funcXXX(); + XXXfunc(); + XXXfuncXXX(); + })")); + } + { + SCOPED_TRACE("lookbehind and lookahead matches"); + EXPECT_TRUE(tooling::runToolOnCode( + std::unique_ptr( + new CallDescriptionAction<>({{{CDF_MaybeBuiltin, "func"}, true}})), + R"( + void func(); + void func_XXX(); + void XXX_func(); + void XXX_func_XXX(); + + void test() { + func(); // exact match + func_XXX(); + XXX_func(); + XXX_func_XXX(); + })")); + } } } // namespace