Index: clang/lib/Analysis/UnsafeBufferUsage.cpp =================================================================== --- clang/lib/Analysis/UnsafeBufferUsage.cpp +++ clang/lib/Analysis/UnsafeBufferUsage.cpp @@ -2341,6 +2341,19 @@ } } + // Remove a `FixableGadget` if the associated variable is not in the graph + // computed above. We do not want to generate fix-its for such variables, + // since they are neither warned nor reachable from a warned one. + for (auto I = FixablesForAllVars.byVar.begin(); + I != FixablesForAllVars.byVar.end();) { + // Note `VisitedVars` contain all the variables in the graph: + if (VisitedVars.find((*I).first) == VisitedVars.end()) { + // no such var in graph: + I = FixablesForAllVars.byVar.erase(I); + } else + ++I; + } + Strategy NaiveStrategy = getNaiveStrategy(UnsafeVars); FixItsForVariableGroup = Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma-fixit.cpp =================================================================== --- clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma-fixit.cpp +++ clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma-fixit.cpp @@ -107,3 +107,128 @@ p[5]; // not to note since the associated warning is suppressed } + + +// Test suppressing interacts with variable grouping: + +// The implication edges are: `a` -> `b` -> `c`. +// If the unsafe operation on `a` is supressed, none of the variables +// will be fixed. +void suppressedVarInGroup() { + int * a; + // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]: + int * b; + // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]: + int * c; + // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]: + +#pragma clang unsafe_buffer_usage begin + a[5] = 5; +#pragma clang unsafe_buffer_usage end + a = b; + b = c; +} + +// To show that if `a[5]` is not suppressed in the +// `suppressedVarInGroup` function above, all variables will be fixed. +void suppressedVarInGroup_control() { + int * a; + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span a" + int * b; + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span b" + int * c; + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span c" + + a[5] = 5; + a = b; + b = c; +} + +// The implication edges are: `a` -> `b` -> `c`. +// The unsafe operation on `b` is supressed, while the unsafe +// operation on `a` is not suppressed. Variable `b` will still be +// fixed when fixing `a`. +void suppressedVarInGroup2() { + int * a; + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span a" + int * b; + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span b" + int * c; + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span c" + + a[5] = 5; +#pragma clang unsafe_buffer_usage begin + b[5] = 5; +#pragma clang unsafe_buffer_usage end + a = b; + b = c; +} + +// The implication edges are: `a` -> `b` -> `c`. +// The unsafe operation on `b` is supressed, while the unsafe +// operation on `c` is not suppressed. Only variable `c` will be fixed +// then. +void suppressedVarInGroup3() { + int * a; + // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]: + int * b; + // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]: + int * c; + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span c" + + c[5] = 5; +#pragma clang unsafe_buffer_usage begin + b[5] = 5; +#pragma clang unsafe_buffer_usage end + a = b; + b = c; +} + +// The implication edges are: `a` -> `b` -> `c` -> `a`. +// The unsafe operation on `b` is supressed, while the unsafe +// operation on `c` is not suppressed. Since the implication graph +// forms a cycle, all variables will be fixed. +void suppressedVarInGroup4() { + int * a; + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span a" + int * b; + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span b" + int * c; + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span c" + + c[5] = 5; +#pragma clang unsafe_buffer_usage begin + b[5] = 5; +#pragma clang unsafe_buffer_usage end + a = b; + b = c; + c = a; +} + +// There are two groups: `a` -> `b` -> `c` and `d` -> `e` -> `f`. +// Suppressing unsafe operations on variables in one group does not +// affect other groups. +void suppressedVarInGroup5() { + int * a; + // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]: + int * b; + // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]: + int * c; + // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]: + int * d; + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span d" + int * e; + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span e" + int * f; + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span f" + +#pragma clang unsafe_buffer_usage begin + a[5] = 5; +#pragma clang unsafe_buffer_usage end + a = b; + b = c; + + d[5] = 5; + d = e; + e = f; +}