Index: clang/lib/Analysis/UnsafeBufferUsage.cpp =================================================================== --- clang/lib/Analysis/UnsafeBufferUsage.cpp +++ clang/lib/Analysis/UnsafeBufferUsage.cpp @@ -152,7 +152,9 @@ // A UPC can be // 1. an argument of a function call (except the callee has [[unsafe_...]] // attribute), or - // 2. the operand of a cast operation; or + // 2. the operand of a cast-to-(Integer or Boolean) operation; or + // 3. the operand of a pointer subtraction operation; or + // 4. the operand of a pointer comparison operation; or // ... auto CallArgMatcher = callExpr(forEachArgumentWithParam(InnerMatcher, @@ -160,15 +162,27 @@ unless(callee(functionDecl(hasAttr(attr::UnsafeBufferUsage))))); auto CastOperandMatcher = - explicitCastExpr(hasCastKind(CastKind::CK_PointerToIntegral), - castSubExpr(allOf(hasPointerType(), InnerMatcher))); + castExpr(anyOf(hasCastKind(CastKind::CK_PointerToIntegral), + hasCastKind(CastKind::CK_PointerToBoolean)), + castSubExpr(allOf(hasPointerType(), InnerMatcher))); + + // A matcher that matches pointer subtractions: + auto PtrSubtractionMatcher = + binaryOperator(hasOperatorName("-"), + // Note that here we need both LHS and RHS to be + // pointer. Then the inner matcher can match any of + // them: + allOf(hasLHS(hasPointerType()), + hasRHS(hasPointerType())), + eachOf(hasLHS(InnerMatcher), + hasRHS(InnerMatcher))); auto CompOperandMatcher = binaryOperator(hasAnyOperatorName("!=", "==", "<", "<=", ">", ">="), eachOf(hasLHS(allOf(hasPointerType(), InnerMatcher)), hasRHS(allOf(hasPointerType(), InnerMatcher)))); - - return stmt(anyOf(CallArgMatcher, CastOperandMatcher, CompOperandMatcher)); + + return stmt(anyOf(CallArgMatcher, CastOperandMatcher, CompOperandMatcher, PtrSubtractionMatcher)); // FIXME: any more cases? (UPC excludes the RHS of an assignment. For now we // don't have to check that.) } Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-addressof-arraysubscript.cpp =================================================================== --- clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-addressof-arraysubscript.cpp +++ clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-addressof-arraysubscript.cpp @@ -13,6 +13,24 @@ // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:37-[[@LINE-1]]:42}:"(p.data() + x)" } +void address_to_bool(int x) { + int * p = new int[10]; + bool a = (bool) &p[5]; + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:19-[[@LINE-1]]:24}:"(p.data() + 5)" + bool b = (bool) &p[x]; + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:19-[[@LINE-1]]:24}:"(p.data() + x)" + + bool a1 = &p[5]; + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:13-[[@LINE-1]]:18}:"(p.data() + 5)" + bool b1 = &p[x]; + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:13-[[@LINE-1]]:18}:"(p.data() + x)" + + if (&p[5]) { + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:7-[[@LINE-1]]:12}:"(p.data() + 5)" + return; + } +} + void call_argument(int x) { int * p = new int[10]; @@ -31,9 +49,9 @@ &p[x]); int * q = new int[10]; - // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:12}:"std::span q" - // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:13-[[@LINE-2]]:13}:"{" - // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:24-[[@LINE-3]]:24}:", 10}" + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:12}:"std::span q" + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:13-[[@LINE-2]]:13}:"{" + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:24-[[@LINE-3]]:24}:", 10}" unsafe_f((unsigned long) &q[5], // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:28-[[@LINE-1]]:33}:"(q.data() + 5)" (void*)0); @@ -45,8 +63,29 @@ // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:37-[[@LINE-1]]:42}:"(p.data() + 5)" } + +void pointer_subtraction(int x) { + int * p = new int[10]; + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:12}:"std::span p" + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:13-[[@LINE-2]]:13}:"{" + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:24-[[@LINE-3]]:24}:", 10}" + + int n = &p[9] - &p[4]; + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:11-[[@LINE-1]]:16}:"(p.data() + 9)" + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:19-[[@LINE-2]]:24}:"(p.data() + 4)" + if (&p[9] - &p[x]) { + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:7-[[@LINE-1]]:12}:"(p.data() + 9)" + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:15-[[@LINE-2]]:20}:"(p.data() + x)" + return; + } +} + + // To test multiple function declarations, each of which carries -// different incomplete informations: +// different incomplete informations. +// no fix-it in the rest of this test: + +// CHECK-NOT: fix-it:"{{.*}}":{ [[clang::unsafe_buffer_usage]] void unsafe_g(void*); @@ -54,9 +93,7 @@ void multiple_unsafe_fundecls() { int * p = new int[10]; - // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]] unsafe_g(&p[5]); - // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]] } void unsafe_h(void*); @@ -68,7 +105,5 @@ void multiple_unsafe_fundecls2() { int * p = new int[10]; - // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]] unsafe_h(&p[5]); - // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]] }