mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-30 04:16:08 +00:00

This commit extends the class `SValBuilder` with the methods `getMinValue()` and `getMaxValue()` to that work like `SValBuilder::getKnownValue()` but return the minimal/maximal possible value the `SVal` is not perfectly constrained. This extension of the ConstraintManager API is discussed at: https://discourse.llvm.org/t/expose-the-inferred-range-information-in-warning-messages/75192 As a simple proof-of-concept application of this new API, this commit extends a message from `core.BitwiseShift` with some range information that reports the assumptions of the analyzer. My main motivation for adding these methods is that I'll also want to use them in `ArrayBoundCheckerV2` to make the error messages less awkward, but I'm starting with this simpler and less important usecase because I want to avoid merge conflicts with my other commit https://github.com/llvm/llvm-project/pull/72107 which is currently under review. The testcase `too_large_right_operand_compound()` shows a situation where querying the range information does not work (and the extra information is not added to the error message). This also affects the debug utility `clang_analyzer_value()`, so the problem isn't in the fresh code. I'll do some investigations to resolve this, but I think that this commit is a step forward even with this limitation.
169 lines
7.3 KiB
C
169 lines
7.3 KiB
C
// RUN: %clang_analyze_cc1 -analyzer-checker=core.BitwiseShift \
|
|
// RUN: -analyzer-checker=debug.ExprInspection \
|
|
// RUN: -analyzer-output=text -verify \
|
|
// RUN: -triple x86_64-pc-linux-gnu -x c %s \
|
|
// RUN: -Wno-shift-count-negative -Wno-shift-negative-value \
|
|
// RUN: -Wno-shift-count-overflow -Wno-shift-overflow \
|
|
// RUN: -Wno-shift-sign-overflow
|
|
//
|
|
// RUN: %clang_analyze_cc1 -analyzer-checker=core.BitwiseShift \
|
|
// RUN: -analyzer-checker=debug.ExprInspection \
|
|
// RUN: -analyzer-config core.BitwiseShift:Pedantic=true \
|
|
// RUN: -analyzer-output=text -verify \
|
|
// RUN: -triple x86_64-pc-linux-gnu -x c++ -std=c++20 %s \
|
|
// RUN: -Wno-shift-count-negative -Wno-shift-negative-value \
|
|
// RUN: -Wno-shift-count-overflow -Wno-shift-overflow \
|
|
// RUN: -Wno-shift-sign-overflow
|
|
//
|
|
// This test file verifies the default behavior of the BitwiseShift checker,
|
|
// which reports the serious logical defects, but doesn't warn on code that's
|
|
// legal under C++20 (or later) and widely accepted (but theoretically
|
|
// undefined) in other compilation modes.
|
|
|
|
// TEST NEGATIVE RIGHT OPERAND
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
int negative_right_operand_literal(void) {
|
|
return 2 << -2;
|
|
// expected-warning@-1 {{Right operand is negative in left shift}}
|
|
// expected-note@-2 {{The result of left shift is undefined because the right operand is negative}}
|
|
}
|
|
|
|
int negative_right_operand_symbolic(int left, int right) {
|
|
// expected-note@+2 {{Assuming 'right' is < 0}}
|
|
// expected-note@+1 {{Taking false branch}}
|
|
if (right >= 0)
|
|
return 0;
|
|
return left >> right;
|
|
// expected-warning@-1 {{Right operand is negative in right shift}}
|
|
// expected-note@-2 {{The result of right shift is undefined because the right operand is negative}}
|
|
}
|
|
|
|
int negative_right_operand_compound(short arg) {
|
|
// expected-note@+2 {{Assuming 'arg' is < 2}}
|
|
// expected-note@+1 {{Taking false branch}}
|
|
if (arg >= 2 )
|
|
return 0;
|
|
return 2 << (arg - 1 - 1 - 1);
|
|
// expected-warning@-1 {{Right operand is negative in left shift}}
|
|
// expected-note@-2 {{The result of left shift is undefined because the right operand is negative}}
|
|
}
|
|
|
|
// TEST TOO LARGE RIGHT OPERAND
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
int too_large_right_operand_literal(void) {
|
|
return 2 << 32;
|
|
// expected-warning@-1 {{Left shift by '32' overflows the capacity of 'int'}}
|
|
// expected-note@-2 {{The result of left shift is undefined because the right operand '32' is not smaller than 32, the capacity of 'int'}}
|
|
}
|
|
|
|
int too_large_right_operand_exact_symbolic(int arg) {
|
|
// expected-note@+4 {{Assuming 'arg' is > 33}}
|
|
// expected-note@+3 {{Left side of '||' is false}}
|
|
// expected-note@+2 {{Assuming 'arg' is < 35}}
|
|
// expected-note@+1 {{Taking false branch}}
|
|
if (arg <= 33 || arg >= 35)
|
|
return 0;
|
|
return 3 << arg;
|
|
// expected-warning@-1 {{Left shift by '34' overflows the capacity of 'int'}}
|
|
// expected-note@-2 {{The result of left shift is undefined because the right operand '34' is not smaller than 32, the capacity of 'int'}}
|
|
}
|
|
|
|
int too_large_right_operand_exact_symbolic_2(char arg) {
|
|
// expected-note@+2 {{Assuming the condition is false}}
|
|
// expected-note@+1 {{Taking false branch}}
|
|
if (arg != ' ')
|
|
return 0;
|
|
return 3 << arg;
|
|
// expected-warning@-1 {{Left shift by '32' overflows the capacity of 'int'}}
|
|
// expected-note@-2 {{The result of left shift is undefined because the right operand '32' is not smaller than 32, the capacity of 'int'}}
|
|
}
|
|
|
|
int too_large_right_operand_symbolic(int left, int right) {
|
|
// expected-note@+2 {{Assuming 'right' is > 31}}
|
|
// expected-note@+1 {{Taking false branch}}
|
|
if (right <= 31)
|
|
return 0;
|
|
return left >> right;
|
|
// expected-warning@-1 {{Right shift overflows the capacity of 'int'}}
|
|
// expected-note@-2 {{The result of right shift is undefined because the right operand is >= 32, not smaller than 32, the capacity of 'int'}}
|
|
}
|
|
|
|
void clang_analyzer_value(int);
|
|
int too_large_right_operand_compound(unsigned short arg) {
|
|
// Note: this would be valid code with an 'unsigned int' because
|
|
// unsigned addition is allowed to overflow.
|
|
clang_analyzer_value(32+arg);
|
|
// expected-warning@-1 {{32s:{ [-2147483648, 2147483647] }}
|
|
// expected-note@-2 {{32s:{ [-2147483648, 2147483647] }}
|
|
return 1 << (32 + arg);
|
|
// expected-warning@-1 {{Left shift overflows the capacity of 'int'}}
|
|
// expected-note@-2 {{The result of left shift is undefined because the right operand is not smaller than 32, the capacity of 'int'}}
|
|
// FIXME: this message should be
|
|
// {{The result of left shift is undefined because the right operand is >= 32, not smaller than 32, the capacity of 'int'}}
|
|
// but for some reason neither the new logic, nor debug.ExprInspection and
|
|
// clang_analyzer_value reports this range information.
|
|
}
|
|
|
|
// TEST STATE UPDATES
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
void state_update(char a, int *p) {
|
|
// NOTE: with 'int a' this would not produce a bug report because the engine
|
|
// would not rule out an overflow.
|
|
*p += 1 << a;
|
|
// expected-note@-1 {{Assuming right operand of bit shift is non-negative but less than 32}}
|
|
*p += 1 << (a + 32);
|
|
// expected-warning@-1 {{Left shift overflows the capacity of 'int'}}
|
|
// expected-note@-2 {{The result of left shift is undefined because the right operand is not smaller than 32, the capacity of 'int'}}
|
|
}
|
|
|
|
void state_update_2(char a, int *p) {
|
|
*p += 1234 >> (a + 32);
|
|
// expected-note@-1 {{Assuming right operand of bit shift is non-negative but less than 32}}
|
|
*p += 1234 >> a;
|
|
// expected-warning@-1 {{Right operand is negative in right shift}}
|
|
// expected-note@-2 {{The result of right shift is undefined because the right operand is negative}}
|
|
}
|
|
|
|
// TEST EXPRESSION TRACKING
|
|
//===----------------------------------------------------------------------===//
|
|
// Expression tracking a "generic" tool that's used by many other checkers,
|
|
// so this is just a minimal test to see that it's activated.
|
|
|
|
void setValue(unsigned *p, unsigned newval) {
|
|
*p = newval;
|
|
// expected-note@-1 {{The value 33 is assigned to 'right'}}
|
|
}
|
|
|
|
int expression_tracked_back(void) {
|
|
unsigned left = 115; // expected-note {{'left' initialized to 115}}
|
|
unsigned right;
|
|
setValue(&right, 33);
|
|
// expected-note@-1 {{Calling 'setValue'}}
|
|
// expected-note@-2 {{Passing the value 33 via 2nd parameter 'newval'}}
|
|
// expected-note@-3 {{Returning from 'setValue'}}
|
|
|
|
return left << right;
|
|
// expected-warning@-1 {{Left shift by '33' overflows the capacity of 'unsigned int'}}
|
|
// expected-note@-2 {{The result of left shift is undefined because the right operand '33' is not smaller than 32, the capacity of 'unsigned int'}}
|
|
}
|
|
|
|
// TEST PERMISSIVENESS
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
int allow_overflows_and_negative_operands(void) {
|
|
// These are all legal under C++ 20 and many compilers accept them under
|
|
// earlier standards as well.
|
|
int int_min = 1 << 31; // no-warning
|
|
int this_overflows = 1027 << 30; // no-warning
|
|
return (-2 << 5) + (-3 >> 4); // no-warning
|
|
}
|
|
|
|
int double_negative(void) {
|
|
return -2 >> -2;
|
|
// expected-warning@-1 {{Right operand is negative in right shift}}
|
|
// expected-note@-2 {{The result of right shift is undefined because the right operand is negative}}
|
|
}
|