diff --git a/.travis.yml b/.travis.yml index 3542fc3..5ff0c21 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ matrix: - os: linux language: cpp compiler: gcc - env: COMPILER="g++-5" CXX_STANDARD=11 TOML_HEAD=OFF + env: COMPILER="g++-5" CXX_STANDARD=11 addons: apt: sources: @@ -17,7 +17,7 @@ matrix: - os: linux language: cpp compiler: gcc - env: COMPILER="g++-6" CXX_STANDARD=11 TOML_HEAD=OFF + env: COMPILER="g++-6" CXX_STANDARD=11 addons: apt: sources: @@ -29,7 +29,7 @@ matrix: - os: linux language: cpp compiler: gcc - env: COMPILER="g++-7" CXX_STANDARD=11 TOML_HEAD=OFF + env: COMPILER="g++-7" CXX_STANDARD=11 addons: apt: sources: @@ -41,7 +41,7 @@ matrix: - os: linux language: cpp compiler: gcc - env: COMPILER="g++-8" CXX_STANDARD=11 TOML_HEAD=OFF + env: COMPILER="g++-8" CXX_STANDARD=11 addons: apt: sources: @@ -65,7 +65,7 @@ matrix: - os: linux language: cpp compiler: gcc - env: COMPILER="g++-8" CXX_STANDARD=17 TOML_HEAD=OFF + env: COMPILER="g++-8" CXX_STANDARD=17 addons: apt: sources: @@ -89,7 +89,7 @@ matrix: - os: linux language: cpp compiler: clang - env: COMPILER="clang++-3.9" CXX_STANDARD=11 TOML_HEAD=OFF + env: COMPILER="clang++-3.9" CXX_STANDARD=11 addons: apt: sources: @@ -103,7 +103,7 @@ matrix: - os: linux language: cpp compiler: clang - env: COMPILER="clang++-4.0" CXX_STANDARD=11 TOML_HEAD=OFF + env: COMPILER="clang++-4.0" CXX_STANDARD=11 addons: apt: sources: @@ -117,7 +117,7 @@ matrix: - os: linux language: cpp compiler: clang - env: COMPILER="clang++-5.0" CXX_STANDARD=11 TOML_HEAD=OFF + env: COMPILER="clang++-5.0" CXX_STANDARD=11 addons: apt: sources: @@ -131,7 +131,7 @@ matrix: - os: linux language: cpp compiler: clang - env: COMPILER="clang++-6.0" CXX_STANDARD=11 TOML_HEAD=OFF + env: COMPILER="clang++-6.0" CXX_STANDARD=11 addons: apt: sources: @@ -145,7 +145,7 @@ matrix: - os: linux language: cpp compiler: clang - env: COMPILER="clang++-7" CXX_STANDARD=11 TOML_HEAD=OFF + env: COMPILER="clang++-7" CXX_STANDARD=11 addons: apt: sources: @@ -159,7 +159,7 @@ matrix: - os: linux language: cpp compiler: clang - env: COMPILER="clang++-8" CXX_STANDARD=11 TOML_HEAD=OFF + env: COMPILER="clang++-8" CXX_STANDARD=11 addons: apt: sources: @@ -187,7 +187,7 @@ matrix: - os: linux language: cpp compiler: clang - env: COMPILER="clang++-8" CXX_STANDARD=17 TOML_HEAD=OFF + env: COMPILER="clang++-8" CXX_STANDARD=17 addons: apt: sources: @@ -212,6 +212,34 @@ matrix: - clang-8 - g++-8 - boost1.70 + - os: linux + language: cpp + compiler: clang + env: COMPILER="clang++-8" CXX_STANDARD=11 WITH_ASAN=ON + addons: + apt: + sources: + - sourceline: 'ppa:ubuntu-toolchain-r/test' + - sourceline: 'ppa:mhier/libboost-latest' + - llvm-toolchain-trusty-8 + packages: + - clang-8 + - g++-8 + - boost1.70 + - os: linux + language: cpp + compiler: clang + env: COMPILER="clang++-8" CXX_STANDARD=11 WITH_UBSAN=ON + addons: + apt: + sources: + - sourceline: 'ppa:ubuntu-toolchain-r/test' + - sourceline: 'ppa:mhier/libboost-latest' + - llvm-toolchain-trusty-8 + packages: + - clang-8 + - g++-8 + - boost1.70 - os: osx language: cpp compiler: clang @@ -234,11 +262,26 @@ script: tar xf cmake-3.14.5-Linux-x86_64.tar.gz -C cmake --strip-components=1 export PATH=${TRAVIS_BUILD_DIR}/cmake/bin:${PATH} fi +- | + if [[ "${TOML_HEAD}" != "ON" ]]; then + export TOML_HEAD="OFF" + fi +- echo "TOML_HEAD = ${TOML_HEAD}" +- | + if [[ "${WITH_ASAN}" != "ON" ]]; then + export WITH_ASAN="OFF" + fi +- echo "WITH_ASAN = ${WITH_ASAN}" +- | + if [[ "${WITH_UBSAN}" != "ON" ]]; then + export WITH_UBSAN="OFF" + fi +- echo "WITH_UBSAN = ${WITH_UBSAN}" - cmake --version - mkdir build - cd build - git clone https://github.com/toml-lang/toml.git -- cmake -DCMAKE_CXX_COMPILER=$COMPILER -DCMAKE_CXX_STANDARD=$CXX_STANDARD -DTOML11_USE_UNRELEASED_TOML_FEATURES=${TOML_HEAD} .. +- cmake -DCMAKE_CXX_COMPILER=$COMPILER -DCMAKE_CXX_STANDARD=$CXX_STANDARD -DTOML11_USE_UNRELEASED_TOML_FEATURES=${TOML_HEAD} -Dtoml11_TEST_WITH_ASAN=${WITH_ASAN} -Dtoml11_TEST_WITH_UBSAN=${WITH_UBSAN} .. - make - ctest --output-on-failure diff --git a/CMakeLists.txt b/CMakeLists.txt index e0315eb..f75ffcc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,8 @@ set(toml11_VERSION ) option(toml11_BUILD_TEST "Build toml tests" ON) +option(toml11_TEST_WITH_ASAN "use LLVM address sanitizer" OFF) +option(toml11_TEST_WITH_UBSAN "use LLVM undefined behavior sanitizer" OFF) include(CheckCXXCompilerFlag) if("${CMAKE_VERSION}" VERSION_GREATER 3.1) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index e527e04..8570aeb 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -104,6 +104,19 @@ foreach(TEST_NAME ${TEST_NAMES}) add_executable(${TEST_NAME} ${TEST_NAME}.cpp) target_link_libraries(${TEST_NAME} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY} toml11::toml11) target_include_directories(${TEST_NAME} PRIVATE ${Boost_INCLUDE_DIRS}) + + if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + if(toml11_TEST_WITH_ASAN) + set_target_properties(${TEST_NAME} PROPERTIES + COMPILE_FLAGS "-fsanitize=address -fno-omit-frame-pointer" + LINK_FLAGS "-fsanitize=address -fno-omit-frame-pointer") + elseif(toml11_TEST_WITH_UBSAN) + set_target_properties(${TEST_NAME} PROPERTIES + COMPILE_FLAGS "-fsanitize=undefined" + LINK_FLAGS "-fsanitize=undefined") + endif() + endif() + add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME} WORKING_DIRECTORY ${PROJECT_BINARY_DIR}) # Set the PATH to be able to find Boost DLL diff --git a/tests/test_parse_inline_table.cpp b/tests/test_parse_inline_table.cpp index e1934a5..5fa99c1 100644 --- a/tests/test_parse_inline_table.cpp +++ b/tests/test_parse_inline_table.cpp @@ -46,3 +46,19 @@ BOOST_AUTO_TEST_CASE(test_inline_table_value) TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "{type.name = \"pug\"}", value(t)); } } + +BOOST_AUTO_TEST_CASE(test_inline_table_immutability) +{ + { + std::istringstream stream(std::string( + "a = {b = 1}\n" + "a.c = 2\n")); + BOOST_CHECK_THROW(toml::parse(stream), toml::syntax_error); + } + { + std::istringstream stream(std::string( + "a = {b = {c = 1}}\n" + "a.b.d = 2\n")); + BOOST_CHECK_THROW(toml::parse(stream), toml::syntax_error); + } +} diff --git a/toml/parser.hpp b/toml/parser.hpp index 9e511ad..7cf644a 100644 --- a/toml/parser.hpp +++ b/toml/parser.hpp @@ -1331,7 +1331,7 @@ insert_nested_key(typename Value::table_type& root, const Value& v, tab->insert(std::make_pair(k, v)); return ok(true); } - else + else // k is not the last one, we should insert recursively { // if there is no corresponding value, insert it first. // related: you don't need to write @@ -1347,6 +1347,27 @@ insert_nested_key(typename Value::table_type& root, const Value& v, // type checking... if(tab->at(k).is_table()) { + // According to toml-lang/toml:36d3091b3 "Clarify that inline + // tables are immutable", check if it adds key-value pair to an + // inline table. + // This is one of the unreleased (after-0.5.0) toml feature. + // But this is marked as "Clarify", so TOML-lang intended that + // inline tables are immutable in all version. + { + // here, if the value is a (multi-line) table, the region + // should be something like `[table-name]`. + if(get_region(tab->at(k)).front() == '{') + { + throw syntax_error(format_underline(concat_to_string( + "toml::insert_value: inserting to an inline table (", + format_dotted_keys(first, std::next(iter)), + ") but inline tables are immutable"), { + {std::addressof(get_region(tab->at(k))), + "inline tables are immutable"}, + {std::addressof(get_region(v)), "inserting this"} + }), v.location()); + } + } tab = std::addressof((*tab)[k].as_table()); } else if(tab->at(k).is_array()) // inserting to array-of-tables? diff --git a/toml/region.hpp b/toml/region.hpp index 973bf48..bf9ba12 100644 --- a/toml/region.hpp +++ b/toml/region.hpp @@ -41,6 +41,7 @@ struct region_base region_base& operator=(region_base&& ) = default; virtual bool is_ok() const noexcept {return false;} + virtual char front() const noexcept {return '\0';} virtual std::string str() const {return std::string("unknown region");} virtual std::string name() const {return std::string("unknown file");} @@ -89,6 +90,7 @@ struct location final : public region_base ~location() = default; bool is_ok() const noexcept override {return static_cast(source_);} + char front() const noexcept override {return *iter_;} // this const prohibits codes like `++(loc.iter())`. const const_iterator iter() const noexcept {return iter_;} @@ -240,6 +242,7 @@ struct region final : public region_base } bool is_ok() const noexcept override {return static_cast(source_);} + char front() const noexcept override {return *first_;} std::string str() const override {return make_string(first_, last_);} std::string line() const override