Index: clang/lib/Analysis/UnsafeBufferUsage.cpp =================================================================== --- clang/lib/Analysis/UnsafeBufferUsage.cpp +++ clang/lib/Analysis/UnsafeBufferUsage.cpp @@ -44,9 +44,10 @@ MatchDescendantVisitor(const internal::DynTypedMatcher *Matcher, internal::ASTMatchFinder *Finder, internal::BoundNodesTreeBuilder *Builder, - internal::ASTMatchFinder::BindKind Bind) + internal::ASTMatchFinder::BindKind Bind, + const bool ignoreUnevaluatedContext) : Matcher(Matcher), Finder(Finder), Builder(Builder), Bind(Bind), - Matches(false) {} + Matches(false), ignoreUnevaluatedContext(ignoreUnevaluatedContext) {} // Returns true if a match is found in a subtree of `DynNode`, which belongs // to the same callable of `DynNode`. @@ -78,6 +79,48 @@ // Traverse descendants return VisitorBase::TraverseDecl(Node); } + + bool TraverseGenericSelectionExpr(GenericSelectionExpr *Node) { + // These are unevaluated, except the result expression. + if(ignoreUnevaluatedContext) + return TraverseStmt(Node->getResultExpr()); + return VisitorBase::TraverseGenericSelectionExpr(Node); + } + + bool TraverseUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *Node) { + // Unevaluated context. + if(ignoreUnevaluatedContext) + return true; + return VisitorBase::TraverseUnaryExprOrTypeTraitExpr(Node); + } + + bool TraverseTypeOfExprTypeLoc(TypeOfExprTypeLoc Node) { + // Unevaluated context. + if(ignoreUnevaluatedContext) + return true; + return VisitorBase::TraverseTypeOfExprTypeLoc(Node); + } + + bool TraverseDecltypeTypeLoc(DecltypeTypeLoc Node) { + // Unevaluated context. + if(ignoreUnevaluatedContext) + return true; + return VisitorBase::TraverseDecltypeTypeLoc(Node); + } + + bool TraverseCXXNoexceptExpr(CXXNoexceptExpr *Node) { + // Unevaluated context. + if(ignoreUnevaluatedContext) + return true; + return VisitorBase::TraverseCXXNoexceptExpr(Node); + } + + bool TraverseCXXTypeidExpr(CXXTypeidExpr *Node) { + // Unevaluated context. + if(ignoreUnevaluatedContext) + return true; + return VisitorBase::TraverseCXXTypeidExpr(Node); + } bool TraverseStmt(Stmt *Node, DataRecursionQueue *Queue = nullptr) { if (!Node) @@ -120,6 +163,7 @@ internal::BoundNodesTreeBuilder ResultBindings; const internal::ASTMatchFinder::BindKind Bind; bool Matches; + bool ignoreUnevaluatedContext; }; // Because we're dealing with raw pointers, let's define what we mean by that. @@ -129,13 +173,21 @@ static auto hasArrayType() { return hasType(hasCanonicalType(arrayType())); } -AST_MATCHER_P(Stmt, forEveryDescendant, internal::Matcher, innerMatcher) { +AST_MATCHER_P(Stmt, forEveryDescendantEvaluatedStmt, internal::Matcher, innerMatcher) { + const DynTypedMatcher &DTM = static_cast(innerMatcher); + + MatchDescendantVisitor Visitor(&DTM, Finder, Builder, ASTMatchFinder::BK_All, true); + return Visitor.findMatch(DynTypedNode::create(Node)); +} + +AST_MATCHER_P(Stmt, forEveryDescendantStmt, internal::Matcher, innerMatcher) { const DynTypedMatcher &DTM = static_cast(innerMatcher); - MatchDescendantVisitor Visitor(&DTM, Finder, Builder, ASTMatchFinder::BK_All); + MatchDescendantVisitor Visitor(&DTM, Finder, Builder, ASTMatchFinder::BK_All, false); return Visitor.findMatch(DynTypedNode::create(Node)); } + // Matches a `Stmt` node iff the node is in a safe-buffer opt-out region AST_MATCHER_P(Stmt, notInSafeBufferOptOut, const UnsafeBufferUsageHandler *, Handler) { @@ -941,32 +993,31 @@ // clang-format off M.addMatcher( - stmt(forEveryDescendant( - eachOf( + stmt(eachOf( // A `FixableGadget` matcher and a `WarningGadget` matcher should not disable // each other (they could if they were put in the same `anyOf` group). // We also should make sure no two `FixableGadget` (resp. `WarningGadget`) matchers // match for the same node, so that we can group them // in one `anyOf` group (for better performance via short-circuiting). - stmt(anyOf( + forEveryDescendantStmt(stmt(anyOf( #define FIXABLE_GADGET(x) \ x ## Gadget::matcher().bind(#x), #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def" - // Also match DeclStmts because we'll need them when fixing - // their underlying VarDecls that otherwise don't have - // any backreferences to DeclStmts. - declStmt().bind("any_ds") - )), - stmt(anyOf( + // In parallel, match all DeclRefExprs so that to find out + // whether there are any uncovered by gadgets. + declRefExpr(anyOf(hasPointerType(), hasArrayType()), to(varDecl())).bind("any_dre") + ))), + forEveryDescendantEvaluatedStmt(stmt(anyOf( // Add Gadget::matcher() for every gadget in the registry. #define WARNING_GADGET(x) \ allOf(x ## Gadget::matcher().bind(#x), notInSafeBufferOptOut(&Handler)), #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def" - // In parallel, match all DeclRefExprs so that to find out - // whether there are any uncovered by gadgets. - declRefExpr(anyOf(hasPointerType(), hasArrayType()), to(varDecl())).bind("any_dre") - ))) - )), + // Also match DeclStmts because we'll need them when fixing + // their underlying VarDecls that otherwise don't have + // any backreferences to DeclStmts. + declStmt().bind("any_ds") + )) + ))), &CB ); // clang-format on Index: clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp =================================================================== --- clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp +++ clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp @@ -24,6 +24,14 @@ #else +namespace std { + class type_info; + class bad_cast; + class bad_typeid; +} +using size_t = __typeof(sizeof(int)); +void *malloc(size_t); + void testIncrement(char *p) { // expected-warning{{'p' is an unsafe pointer used for buffer access}} ++p; // expected-note{{used in pointer arithmetic here}} p++; // expected-note{{used in pointer arithmetic here}} @@ -102,10 +110,20 @@ foo(ap4[1]); // expected-note{{used in buffer access here}} } -//TODO: do not warn for unevaluated context -void testUnevaluatedContext(int * p) {// expected-warning{{'p' is an unsafe pointer used for buffer access}} - foo(sizeof(p[1]), // expected-note{{used in buffer access here}} - sizeof(decltype(p[1]))); // expected-note{{used in buffer access here}} +void testUnevaluatedContext(int * p) { // expected-warning{{'p' is an unsafe pointer used for buffer access}} + foo(sizeof(p[1]), // no-note + sizeof(decltype(p[1]))); // no-note + __typeof(p[5]) x; // no-note + int *q = (int *)malloc(sizeof(p[5])); // no-note + int y = sizeof(p[5]); // no-note + __is_pod(__typeof(p[5])); // no-note + __is_trivially_constructible(__typeof(p[5]), decltype(p[5])); // no-note + _Generic(p[1], int: 2, float: 3); // no-note + _Generic(1, int: p[2], float: 3); // expected-note{{used in buffer access here}} + _Generic(1, int: 2, float: p[3]); // no-note + decltype(p[2]) var = y; // no-note + noexcept(p[2]); // no-note + typeid(p[3]); // no-note } void testQualifiedParameters(const int * p, const int * const q, const int a[10], const int b[10][10]) {