llvm-project/clang/test/Analysis/container-modeling.cpp
Adam Balogh 9a08a3fab9 [Analyzer] Split container modeling from iterator modeling
Iterator modeling depends on container modeling,
but not vice versa. This enables the possibility
to arrange these two modeling checkers into
separate layers.

There are several advantages for doing this: the
first one is that this way we can keep the
respective modeling checkers moderately simple
and small. Furthermore, this enables creation of
checkers on container operations which only
depend on the container modeling. Thus iterator
modeling can be disabled together with the
iterator checkers if they are not needed.

Since many container operations also affect
iterators, container modeling also uses the
iterator library: it creates iterator positions
upon calling the `begin()` or `end()` method of
a containter (but propagation of the abstract
position is left to the iterator modeling),
shifts or invalidates iterators according to the
rules upon calling a container modifier and
rebinds the iterator to a new container upon
`std::move()`.

Iterator modeling propagates the abstract
iterator position, handles the relations between
iterator positions and models iterator
operations such as increments and decrements.

Differential Revision: https://reviews.llvm.org/D73547
2020-01-29 16:10:45 +01:00

190 lines
6.7 KiB
C++

// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,cplusplus,debug.DebugIteratorModeling,debug.ExprInspection -analyzer-config aggressive-binary-operation-simplification=true -analyzer-config c++-container-inlining=false %s -verify
// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,cplusplus,debug.DebugIteratorModeling,debug.ExprInspection -analyzer-config aggressive-binary-operation-simplification=true -analyzer-config c++-container-inlining=true -DINLINE=1 %s -verify
// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,cplusplus,alpha.cplusplus.IteratorModeling,debug.ExprInspection -analyzer-config aggressive-binary-operation-simplification=true %s 2>&1 | FileCheck %s
#include "Inputs/system-header-simulator-cxx.h"
template <typename Container>
long clang_analyzer_container_begin(const Container&);
template <typename Container>
long clang_analyzer_container_end(const Container&);
void clang_analyzer_denote(long, const char*);
void clang_analyzer_express(long);
void clang_analyzer_eval(bool);
void clang_analyzer_warnIfReached();
void begin(const std::vector<int> &V) {
V.begin();
clang_analyzer_denote(clang_analyzer_container_begin(V), "$V.begin()");
clang_analyzer_express(clang_analyzer_container_begin(V)); //expected-warning{{$V.begin()}}
}
void end(const std::vector<int> &V) {
V.end();
clang_analyzer_denote(clang_analyzer_container_end(V), "$V.end()");
clang_analyzer_express(clang_analyzer_container_end(V)); //expected-warning{{$V.end()}}
}
////////////////////////////////////////////////////////////////////////////////
///
/// C O N T A I N E R A S S I G N M E N T S
///
////////////////////////////////////////////////////////////////////////////////
// Move
void move_assignment(std::vector<int> &V1, std::vector<int> &V2) {
V1.cbegin();
V1.cend();
V2.cbegin();
V2.cend();
long B1 = clang_analyzer_container_begin(V1);
long E1 = clang_analyzer_container_end(V1);
long B2 = clang_analyzer_container_begin(V2);
long E2 = clang_analyzer_container_end(V2);
V1 = std::move(V2);
clang_analyzer_eval(clang_analyzer_container_begin(V1) == B2); //expected-warning{{TRUE}}
clang_analyzer_eval(clang_analyzer_container_end(V2) == E2); //expected-warning{{TRUE}}
}
////////////////////////////////////////////////////////////////////////////////
///
/// C O N T A I N E R M O D I F I E R S
///
////////////////////////////////////////////////////////////////////////////////
/// push_back()
///
/// Design decision: extends containers to the ->RIGHT-> (i.e. the
/// past-the-end position of the container is incremented).
void push_back(std::vector<int> &V, int n) {
V.cbegin();
V.cend();
clang_analyzer_denote(clang_analyzer_container_begin(V), "$V.begin()");
clang_analyzer_denote(clang_analyzer_container_end(V), "$V.end()");
V.push_back(n);
clang_analyzer_express(clang_analyzer_container_begin(V)); // expected-warning{{$V.begin()}}
clang_analyzer_express(clang_analyzer_container_end(V)); // expected-warning{{$V.end() + 1}}
}
/// emplace_back()
///
/// Design decision: extends containers to the ->RIGHT-> (i.e. the
/// past-the-end position of the container is incremented).
void emplace_back(std::vector<int> &V, int n) {
V.cbegin();
V.cend();
clang_analyzer_denote(clang_analyzer_container_begin(V), "$V.begin()");
clang_analyzer_denote(clang_analyzer_container_end(V), "$V.end()");
V.emplace_back(n);
clang_analyzer_express(clang_analyzer_container_begin(V)); // expected-warning{{$V.begin()}}
clang_analyzer_express(clang_analyzer_container_end(V)); // expected-warning{{$V.end() + 1}}
}
/// pop_back()
///
/// Design decision: shrinks containers to the <-LEFT<- (i.e. the
/// past-the-end position of the container is decremented).
void pop_back(std::vector<int> &V, int n) {
V.cbegin();
V.cend();
clang_analyzer_denote(clang_analyzer_container_begin(V), "$V.begin()");
clang_analyzer_denote(clang_analyzer_container_end(V), "$V.end()");
V.pop_back();
clang_analyzer_express(clang_analyzer_container_begin(V)); // expected-warning{{$V.begin()}}
clang_analyzer_express(clang_analyzer_container_end(V)); // expected-warning{{$V.end() - 1}}
}
/// push_front()
///
/// Design decision: extends containers to the <-LEFT<- (i.e. the first
/// position of the container is decremented).
void push_front(std::deque<int> &D, int n) {
D.cbegin();
D.cend();
clang_analyzer_denote(clang_analyzer_container_begin(D), "$D.begin()");
clang_analyzer_denote(clang_analyzer_container_end(D), "$D.end()");
D.push_front(n);
clang_analyzer_express(clang_analyzer_container_begin(D)); // expected-warning{{$D.begin()}} FIXME: Should be $D.begin() - 1 (to correctly track the container's size)
clang_analyzer_express(clang_analyzer_container_end(D)); // expected-warning{{$D.end()}}
}
/// emplace_front()
///
/// Design decision: extends containers to the <-LEFT<- (i.e. the first
/// position of the container is decremented).
void deque_emplace_front(std::deque<int> &D, int n) {
D.cbegin();
D.cend();
clang_analyzer_denote(clang_analyzer_container_begin(D), "$D.begin()");
clang_analyzer_denote(clang_analyzer_container_end(D), "$D.end()");
D.emplace_front(n);
clang_analyzer_express(clang_analyzer_container_begin(D)); // expected-warning{{$D.begin()}} FIXME: Should be $D.begin - 1 (to correctly track the container's size)
clang_analyzer_express(clang_analyzer_container_end(D)); // expected-warning{{$D.end()}}
}
/// pop_front()
///
/// Design decision: shrinks containers to the ->RIGHT-> (i.e. the first
/// position of the container is incremented).
void deque_pop_front(std::deque<int> &D, int n) {
D.cbegin();
D.cend();
clang_analyzer_denote(clang_analyzer_container_begin(D), "$D.begin()");
clang_analyzer_denote(clang_analyzer_container_end(D), "$D.end()");
D.pop_front();
clang_analyzer_express(clang_analyzer_container_begin(D)); // expected-warning{{$D.begin() + 1}}
clang_analyzer_express(clang_analyzer_container_end(D)); // expected-warning{{$D.end()}}
}
void clang_analyzer_printState();
void print_state(std::vector<int> &V) {
V.cbegin();
clang_analyzer_printState();
// CHECK: "checker_messages": [
// CHECK-NEXT: { "checker": "alpha.cplusplus.ContainerModeling", "messages": [
// CHECK-NEXT: "Container Data :",
// CHECK-NEXT: "SymRegion{reg_$[[#]]<std::vector<int> & V>} : [ conj_$[[#]]{long, LC[[#]], S[[#]], #[[#]]} .. <Unknown> ]"
// CHECK-NEXT: ]}
V.cend();
clang_analyzer_printState();
// CHECK: "checker_messages": [
// CHECK-NEXT: { "checker": "alpha.cplusplus.ContainerModeling", "messages": [
// CHECK-NEXT: "Container Data :",
// CHECK-NEXT: "SymRegion{reg_$[[#]]<std::vector<int> & V>} : [ conj_$[[#]]{long, LC[[#]], S[[#]], #[[#]]} .. conj_$[[#]]{long, LC[[#]], S[[#]], #[[#]]} ]"
// CHECK-NEXT: ]}
}