├── .gitattributes ├── .gitignore ├── .gitlab-ci.yml ├── .gitmodules ├── LICENSE.txt ├── README.md ├── ci ├── ci-functions.sh ├── cirrus-script.sh └── gitlab-script.sh ├── doc └── demo │ ├── ascii_graphics.cpp │ ├── ascii_graphics.h │ ├── cmdparse.h │ └── main.cpp ├── gpl-3.0.txt ├── src ├── CMakeLists.txt ├── DependenciesCollector.cpp ├── DependenciesCollector.h ├── IntervalSet.h ├── MergeNamespacesVisitor.cpp ├── MergeNamespacesVisitor.h ├── OptimizerVisitor.cpp ├── OptimizerVisitor.h ├── RemoveInactivePreprocessorBlocks.cpp ├── RemoveInactivePreprocessorBlocks.h ├── SmartRewriter.cpp ├── SmartRewriter.h ├── SourceInfo.cpp ├── SourceInfo.h ├── SourceLocationComparers.cpp ├── SourceLocationComparers.h ├── Timer.cpp ├── Timer.h ├── caideInliner.cpp ├── caideInliner.h ├── caideInliner.hpp ├── caide_debug.h ├── clang_compat.cpp ├── clang_compat.h ├── clang_version.h ├── cmd │ ├── CMakeLists.txt │ └── cmd.cpp ├── detect_options.cpp ├── detect_options.h ├── inliner.cpp ├── inliner.h ├── optimizer.cpp ├── optimizer.h ├── sema_utils.cpp ├── sema_utils.h ├── test-tool │ ├── CMakeLists.txt │ └── test-tool.cpp ├── util.cpp └── util.h ├── tests ├── cases │ ├── actually-written-type │ │ ├── 1.cpp │ │ ├── clangOptions.txt │ │ └── etalon.cpp │ ├── alias-in-template-argument │ │ ├── 1.cpp │ │ └── etalon.cpp │ ├── base-class-of-template │ │ ├── 1.cpp │ │ └── etalon.cpp │ ├── base-initializers │ │ ├── 1.cpp │ │ ├── clangOptions.txt │ │ └── etalon.cpp │ ├── caide-concept-comment │ │ ├── 1.cpp │ │ └── etalon.cpp │ ├── concepts-constraints │ │ ├── 1.cpp │ │ ├── clangOptions.txt │ │ └── etalon.cpp │ ├── delayed-parsing │ │ ├── 1.cpp │ │ ├── 2.cpp │ │ ├── clangOptions.txt │ │ └── etalon.cpp │ ├── enums │ │ ├── 1.cpp │ │ ├── clangOptions.txt │ │ └── etalon.cpp │ ├── friends │ │ ├── 1.cpp │ │ └── etalon.cpp │ ├── github-issue17 │ │ ├── 1.cpp │ │ └── etalon.cpp │ ├── github-issue4 │ │ ├── 1.cpp │ │ └── etalon.cpp │ ├── github-issue8 │ │ ├── 1.cpp │ │ ├── clangOptions.txt │ │ └── etalon.cpp │ ├── ident-to-keep │ │ ├── 1.cpp │ │ ├── etalon.cpp │ │ └── identifiersToKeep.txt │ ├── include-option-std │ │ ├── 1.cpp │ │ ├── clangOptions.txt │ │ ├── etalon.cpp │ │ └── mystd │ │ │ └── mystd.h │ ├── include-option-user │ │ ├── 1.cpp │ │ ├── clangOptions.txt │ │ ├── etalon.cpp │ │ └── user_header.h │ ├── inheriting-ctor │ │ ├── 1.cpp │ │ ├── clangOptions.txt │ │ └── etalon.cpp │ ├── inliner1 │ │ ├── 1.cpp │ │ ├── 2.cpp │ │ ├── etalon.cpp │ │ └── macrosToKeep.txt │ ├── inliner2 │ │ ├── 1.cpp │ │ ├── 2.cpp │ │ ├── clangOptions.txt │ │ ├── etalon.cpp │ │ ├── system-inc │ │ │ └── myvector │ │ └── user-inc │ │ │ └── inc.h │ ├── inliner3 │ │ ├── 1.cpp │ │ ├── 2.cpp │ │ ├── clangOptions.txt │ │ └── etalon.cpp │ ├── line-directives │ │ ├── 1.cpp │ │ └── etalon.cpp │ ├── macros │ │ ├── 1.cpp │ │ ├── etalon.cpp │ │ └── macrosToKeep.txt │ ├── merge-namespaces-2 │ │ ├── 1.cpp │ │ └── etalon.cpp │ ├── merge-namespaces │ │ ├── 1.cpp │ │ ├── clangOptions.txt │ │ └── etalon.cpp │ ├── pull-headers-up │ │ ├── 1.cpp │ │ ├── clangOptions.txt │ │ ├── etalon.cpp │ │ └── mystd │ │ │ ├── mymap.h │ │ │ └── myvector.h │ ├── qualifiers │ │ ├── 1.cpp │ │ ├── clangOptions.txt │ │ └── etalon.cpp │ ├── references-from-template-arguments │ │ ├── 1.cpp │ │ ├── clangOptions.txt │ │ └── etalon.cpp │ ├── remove-comments │ │ ├── 1.cpp │ │ ├── clangOptions.txt │ │ └── etalon.cpp │ ├── remove-namespaces │ │ ├── 1.cpp │ │ ├── clangOptions.txt │ │ └── etalon.cpp │ ├── remove-template-functions │ │ ├── 1.cpp │ │ └── etalon.cpp │ ├── remove-type-alias │ │ ├── 1.cpp │ │ ├── clangOptions.txt │ │ └── etalon.cpp │ ├── sizeof │ │ ├── 1.cpp │ │ └── etalon.cpp │ ├── source-ranges │ │ ├── 1.cpp │ │ └── etalon.cpp │ ├── static-assert │ │ ├── 1.cpp │ │ ├── clangOptions.txt │ │ └── etalon.cpp │ ├── std-namespace │ │ ├── 1.cpp │ │ ├── clangOptions.txt │ │ ├── etalon.cpp │ │ └── mystd │ │ │ └── mystd.h │ ├── stl │ │ ├── 1.cpp │ │ ├── clangOptions.txt │ │ └── etalon.cpp │ ├── template-alias │ │ ├── 1.cpp │ │ ├── clangOptions.txt │ │ └── etalon.cpp │ ├── template-friend │ │ ├── 1.cpp │ │ └── etalon.cpp │ ├── template-variables │ │ ├── 1.cpp │ │ └── etalon.cpp │ ├── templated-context │ │ ├── 1.cpp │ │ ├── clangOptions.txt │ │ └── etalon.cpp │ ├── track-parent-decls │ │ ├── 1.cpp │ │ ├── clangOptions.txt │ │ └── etalon.cpp │ ├── ull │ │ ├── 1.cpp │ │ └── etalon.cpp │ ├── unused-fields │ │ ├── 1.cpp │ │ └── etalon.cpp │ └── using-declarations │ │ ├── 1.cpp │ │ └── etalon.cpp └── temp │ └── keepme.txt └── tools └── cfapi.py /.gitattributes: -------------------------------------------------------------------------------- 1 | /tests/** linguist-documentation 2 | /doc/** linguist-documentation 3 | /src/llvm-project/** linguist-vendored 4 | /ci/cmake/** linguist-vendored 5 | 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *~ 3 | *.o 4 | *.pyc 5 | .gdb_history 6 | 7 | /build 8 | /tools/cfkeys.py 9 | 10 | /tests/temp/* 11 | !/tests/temp/keepme.txt 12 | 13 | tags 14 | cscope.out 15 | session.vim 16 | 17 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | Build and test: 2 | image: 'debian:buster' 3 | script: ci/gitlab-script.sh 4 | timeout: 3 hours 5 | 6 | cache: 7 | key: "ccache:os=debian-buster:systemclang=$CAIDE_USE_SYSTEM_CLANG:clang=$CAIDE_CLANG_VERSION" 8 | paths: [ 'ccache-cache' ] 9 | 10 | artifacts: 11 | paths: 12 | - cmd 13 | 14 | parallel: 15 | matrix: 16 | - CAIDE_USE_SYSTEM_CLANG: 'OFF' 17 | - CAIDE_USE_SYSTEM_CLANG: 'ON' 18 | # https://apt.llvm.org/llvm.sh installation script only supports versions starting at 9 19 | CAIDE_CLANG_VERSION: [9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20] 20 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "src/llvm-project"] 2 | path = src/llvm-project 3 | url = https://github.com/llvm/llvm-project 4 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | caide C++ Inliner - merge all your C++ source files and headers 2 | into a single C++ file and remove unused code. 3 | Copyright (c) 2014-2025, slycelote 4 | 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Caide C++ inliner 2 | 3 | [![Build 4 | status](https://gitlab.com/slycelote/caide-cpp-inliner/badges/master/pipeline.svg)](https://gitlab.com/slycelote/caide-cpp-inliner/-/commits/master) 5 | 6 | *Caide C++ inliner* transforms a C++ program consisting of multiple source 7 | files and headers into a single self-contained source file without any 8 | external dependencies (except for standard system headers). Unused code is not 9 | included in the resulting file. 10 | 11 | This project is available at the following mirrors: 12 | 13 | * [GitLab](https://gitlab.com/slycelote/caide-cpp-inliner) 14 | * [GitHub](https://github.com/slycelote/caide-cpp-inliner) 15 | 16 | ## Demo 17 | 18 | The directory [doc/demo](./../../tree/master/doc/demo) contains a small sample 19 | application. We will write a program using caideInliner library that processes 20 | the source code of this application: 21 | 22 | 23 | #include "caideInliner.hpp" 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | int main() { 30 | using namespace std; 31 | try { 32 | // (1) 33 | const string tempDirectory = "/tmp"; 34 | caide::CppInliner inliner(tempDirectory); 35 | // (2) 36 | inliner.clangCompilationOptions.push_back("-std=c++11"); 37 | inliner.clangCompilationOptions.push_back("-I"); 38 | inliner.clangCompilationOptions.push_back("."); 39 | // (3) 40 | inliner.clangCompilationOptions.push_back("-isystem"); 41 | #ifdef _WIN32 42 | inliner.clangCompilationOptions.push_back("C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.10150.0\\ucrt"); 43 | #else 44 | inliner.clangCompilationOptions.push_back("../../src/llvm-project/clang/lib/Headers"); 45 | #endif 46 | 47 | // (4) 48 | vector files; 49 | files.push_back("main.cpp"); 50 | files.push_back("ascii_graphics.cpp"); 51 | 52 | // (5) 53 | inliner.inlineCode(files, "result.cpp"); 54 | } catch (const exception& e) { 55 | cerr << e.what() << endl; 56 | return 1; 57 | } 58 | return 0; 59 | } 60 | 61 | 62 | The above code, after including the [caideInliner 63 | header](src/caideInliner.hpp): 64 | 65 | 1. specifies a directory for temporary files (it must already exist) 66 | 2. sets up compilation options (they may vary depending on the application) 67 | 3. adds additional directories containing system header files. It's either a 68 | hardcoded path for VS 2015, or a path to built-in clang headers (available 69 | in clang source tree included in this repository). You may need to tweak 70 | these options further depending on your system. 71 | 4. lists all c++ source files of the application. 72 | 5. finally, runs the inliner and outputs the result in a single c++ file. 73 | 74 | Compile and run this code. (You will need to link against caideInliner library 75 | and several clang and LLVM static libraries.) Alternatively, a [simple 76 | command-line utility](./../../tree/master/src/cmd) is included that you can 77 | run like this: `./cmd -std=c++11 -I . -isystem ../../src/clang/lib/Headers/ -- 78 | main.cpp ascii_graphics.cpp`. 79 | 80 | Inspect the resulting code in the file `result.cpp`. Note how unused parts of 81 | the code (e.g. one of the constructors of `CommandLineParserException` or 82 | inactive preprocessor blocks from `main.cpp`) were eliminated. 83 | 84 | Now add a preprocessor definition `OPT_FRAME`: 85 | 86 | inliner.clangCompilationOptions.push_back("-DOPT_FRAME"); 87 | 88 | and rerun the program. Observe how the output file changes accordingly. 89 | 90 | 91 | ## Build 92 | 93 | clang library is a required dependency. You can either build it from scratch, 94 | or use a version installed in your system. 95 | 96 | * To build from scratch (recommended): 97 | 98 | git submodule update --init --recursive 99 | mkdir build 100 | cd build 101 | cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release ../src 102 | 103 | 104 | Replace the last argument with the full path to [src](./../../tree/master/src) 105 | directory. Replace "Unix Makefiles" with "Visual Studio 16 2019" or another 106 | [generator](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html) 107 | suitable for your platform. Refer to [cmake 108 | documentation](https://cmake.org/cmake/help/latest/manual/cmake.1.html) for 109 | details. 110 | 111 | Once build files are generated, build as usual (run `make`, open the VS 112 | solution etc.) 113 | 114 | * To use clang libraries from your system you need to first install them 115 | (in Linux, search for packages clang-14, libclang-14-dev and llvm-14-dev 116 | or similar). 117 | 118 | 119 | mkdir build 120 | cd build 121 | cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release -DCAIDE_USE_SYSTEM_CLANG=ON ../src 122 | 123 | 124 | When the build is done, run `ctest` to execute the test suite. 125 | 126 | 127 | ## Documentation 128 | 129 | Refer to Doxygen comments in [caideInliner header](src/caideInliner.hpp). 130 | 131 | 132 | ## Known issues 133 | 134 | * Non-standard features, such as defining a function without return type, are 135 | not supported. 136 | * Defining a class and a variable of the same class simultaneously (`struct A 137 | {} a;`) is not supported. 138 | * Unused global variables are removed. This is fine, unless you use such a 139 | variable for side effects. In that case, mark the variable with a comment 140 | `/// caide keep` or `/** caide keep **/` (note the triple slash and the 141 | double asterisk). For example, 142 | 143 | struct SideEffect {...}; 144 | /// caide keep 145 | SideEffect instance; 146 | 147 | In general, if you find that a declaration is removed incorrectly, mark this 148 | declaration with `caide keep` (and file an issue :)). 149 | 150 | -------------------------------------------------------------------------------- /ci/ci-functions.sh: -------------------------------------------------------------------------------- 1 | 2 | ansi_red="\e[0;91m" 3 | ansi_blue="\e[0;94m" 4 | ansi_reset="\e[0m" 5 | 6 | function ci_timer { 7 | echo -e "${ansi_blue} Current time: $(date +%H:%M:%S) ${ansi_reset}" 8 | } 9 | 10 | # Original code at https://github.com/travis-ci/travis-build/blob/52d064e1e1db237633ac9e9417d6c0aa447c97ed/lib/travis/build/bash/travis_retry.bash 11 | # under MIT license 12 | function ci_retry { 13 | local result=0 14 | local count=1 15 | while [[ "${count}" -le 3 ]]; do 16 | [[ "${result}" -ne 0 ]] && { 17 | echo -e "\\n${ansi_red}The command \"${*}\" failed. Retrying, ${count} of 3.${ansi_reset}\\n" >&2 18 | } 19 | # run the command in a way that doesn't disable setting `errexit` 20 | "${@}" 21 | result="${?}" 22 | if [[ $result -eq 0 ]]; then break; fi 23 | count="$((count + 1))" 24 | sleep 1 25 | done 26 | 27 | [[ "${count}" -gt 3 ]] && { 28 | echo -e "\\n${ansi_red}The command \"${*}\" failed 3 times.${ansi_reset}\\n" >&2 29 | } 30 | 31 | return "${result}" 32 | } 33 | 34 | -------------------------------------------------------------------------------- /ci/cirrus-script.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -ev 3 | 4 | . ci/ci-functions.sh 5 | 6 | # Must match the value in .cirrus.yml 7 | export CCACHE_DIR="$PWD/ccache-cache" 8 | 9 | if [ "$CIRRUS_OS" = "linux" ] 10 | then 11 | ci_timer 12 | apt-get update 13 | ci_timer 14 | ci_retry apt-get install -y wget software-properties-common apt-transport-https cmake ninja-build ccache g++-5 gcc-5 15 | export CXX=g++-5 16 | export CC=gcc-5 17 | ci_timer 18 | else 19 | ci_timer 20 | pkg install -y cmake ninja ccache 21 | ci_timer 22 | export CXX=clang++ 23 | export CC=clang 24 | fi 25 | 26 | date 27 | 28 | if [ "$CAIDE_USE_SYSTEM_CLANG" = "ON" ] 29 | then 30 | export Clang_ROOT=/usr/lib/llvm-$CAIDE_CLANG_VERSION 31 | export CMAKE_PREFIX_PATH=$Clang_ROOT 32 | 33 | # Debug 34 | llvm-config-"$CAIDE_CLANG_VERSION" --cxxflags --cflags --ldflags --has-rtti 35 | else 36 | if [ "$CIRRUS_OS" = "linux" ] 37 | then 38 | ci_retry apt-get install -y git 39 | else 40 | pkg install -y git 41 | fi 42 | ci_timer 43 | git submodule sync 44 | git submodule update --init 45 | fi 46 | 47 | env | sort 48 | cmake --version 49 | "$CXX" --version 50 | "$CC" --version 51 | ci_timer 52 | 53 | mkdir build 54 | cd build 55 | cmake -GNinja -DCAIDE_USE_SYSTEM_CLANG=$CAIDE_USE_SYSTEM_CLANG \ 56 | -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER_LAUNCHER=ccache \ 57 | -DCMAKE_BUILD_TYPE=MinSizeRel ../src 58 | 59 | ninja || ninja -j1 60 | 61 | ci_timer 62 | 63 | if [ "$CAIDE_USE_SYSTEM_CLANG" = "OFF" ] 64 | then 65 | ninja install-clang-resource-headers 66 | # The previous target installs builtin clang headers under llvm-project/, but clang libraries expect to find them under lib/ 67 | # (a bug in clang when it's built as a CMake subproject?) 68 | ln -s $(pwd)/llvm-project/llvm/lib/clang lib/clang 69 | fi 70 | 71 | ctest --verbose 72 | 73 | ci_timer 74 | 75 | -------------------------------------------------------------------------------- /ci/gitlab-script.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -ev 3 | 4 | . ci/ci-functions.sh 5 | 6 | # Must match the value in .gitlab-ci.yml 7 | export CCACHE_DIR="$PWD/ccache-cache" 8 | 9 | ci_timer 10 | 11 | apt-get update 12 | ci_timer 13 | 14 | apt-get install -y lsb-release wget software-properties-common gnupg ninja-build ccache g++-7 gcc-7 15 | export CXX=g++-7 16 | export CC=gcc-7 17 | ci_timer 18 | 19 | wget https://github.com/Kitware/CMake/releases/download/v3.20.1/cmake-3.20.1-linux-x86_64.tar.gz 20 | tar xf cmake-3.20.1-linux-x86_64.tar.gz 21 | rm *.tar.gz 22 | export PATH="$PATH":"$PWD/cmake-3.20.1-linux-x86_64/bin" 23 | ci_timer 24 | 25 | if [ "$CAIDE_USE_SYSTEM_CLANG" = "ON" ] 26 | then 27 | wget https://apt.llvm.org/llvm.sh 28 | chmod +x llvm.sh 29 | ./llvm.sh $CAIDE_CLANG_VERSION 30 | ci_timer 31 | apt-get install -y clang-"$CAIDE_CLANG_VERSION" libclang-"$CAIDE_CLANG_VERSION"-dev llvm-"$CAIDE_CLANG_VERSION"-dev 32 | ci_timer 33 | 34 | export Clang_ROOT=/usr/lib/llvm-$CAIDE_CLANG_VERSION 35 | export CMAKE_PREFIX_PATH=$Clang_ROOT 36 | 37 | # Debug 38 | llvm-config-"$CAIDE_CLANG_VERSION" --cxxflags --cflags --ldflags --has-rtti 39 | else 40 | apt-get install -y git 41 | ci_timer 42 | git submodule sync 43 | git submodule update --init 44 | ci_timer 45 | fi 46 | 47 | env | sort 48 | cmake --version 49 | "$CXX" --version 50 | "$CC" --version 51 | 52 | mkdir build 53 | cd build 54 | cmake -GNinja -DCAIDE_USE_SYSTEM_CLANG=$CAIDE_USE_SYSTEM_CLANG \ 55 | -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER_LAUNCHER=ccache \ 56 | -DCMAKE_BUILD_TYPE=MinSizeRel ../src 57 | 58 | # First build may run out of memory 59 | ninja || ninja -j1 60 | ci_timer 61 | 62 | if [ "$CAIDE_USE_SYSTEM_CLANG" = "OFF" ] 63 | then 64 | ninja install-clang-resource-headers 65 | # The previous target installs builtin clang headers under llvm-project/, but clang libraries expect to find them under lib/ 66 | # (a bug in clang when it's built as a CMake subproject?) 67 | ln -s "$PWD"/llvm-project/llvm/lib/clang lib/clang 68 | fi 69 | 70 | ctest --verbose 71 | 72 | ci_timer 73 | 74 | if [ "$CAIDE_USE_SYSTEM_CLANG" = "OFF" ] 75 | then 76 | # Create an artifact only for this job 77 | # Must match the path in .gitlab-ci.yml 78 | cp cmd/cmd .. 79 | fi 80 | 81 | -------------------------------------------------------------------------------- /doc/demo/ascii_graphics.cpp: -------------------------------------------------------------------------------- 1 | #include "ascii_graphics.h" 2 | 3 | namespace ascii { 4 | 5 | void outputInFrame(const std::string& text, std::ostream& out) { 6 | const std::size_t textLength = text.length(); 7 | const std::string asteriscsLine(textLength + 4, '*'); 8 | out << asteriscsLine << "\n" 9 | << "* " << text << " *\n" 10 | << asteriscsLine << std::endl; 11 | } 12 | 13 | } 14 | 15 | -------------------------------------------------------------------------------- /doc/demo/ascii_graphics.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace ascii { 7 | 8 | /// Output the text in a frame made of asteriscs 9 | void outputInFrame(const std::string& text, std::ostream& out); 10 | 11 | } 12 | 13 | 14 | -------------------------------------------------------------------------------- /doc/demo/cmdparse.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | 13 | namespace cmdparse { 14 | 15 | /// An exception occuring during parsing of a command line 16 | class CommandLineParserException: public std::runtime_error 17 | { 18 | public: 19 | explicit CommandLineParserException(const char* message) 20 | : std::runtime_error(message) 21 | {} 22 | 23 | explicit CommandLineParserException(const std::string& message) 24 | : std::runtime_error(message) 25 | {} 26 | }; 27 | 28 | 29 | namespace detail { 30 | template 31 | T parseValue(const std::string& s) { 32 | std::istringstream in(s); 33 | T value; 34 | if (!(in >> value)) 35 | throw CommandLineParserException(std::string("Couldn't parse value ") + s); 36 | return value; 37 | } 38 | 39 | template<> 40 | std::string parseValue(const std::string& s) { 41 | return s; 42 | } 43 | } 44 | 45 | 46 | /// Parses command line arguments into a structure of type Config 47 | template 48 | class CommandLineParser 49 | { 50 | public: 51 | /// Register a command line option. Returns the same instance of CommandLineParser, 52 | /// for 'fluent interface' 53 | template 54 | CommandLineParser& add(const std::string& optionName, T Config::* field) 55 | { 56 | validateOptionName(optionName); 57 | optionHandlers[optionName] = makeOptionHandler(optionName, field); 58 | return *this; 59 | } 60 | 61 | /// Parse command line arguments given in the range [first; last) 62 | template 63 | Config parse(ArgsIterator first, ArgsIterator last) { 64 | config = Config(); 65 | 66 | args.clear(); 67 | std::copy(first, last, std::back_inserter(args)); 68 | 69 | for (argsIterator = args.begin(); argsIterator != args.end(); ++argsIterator) { 70 | const auto& optionName = *argsIterator; 71 | 72 | auto handler = optionHandlers.find(optionName); 73 | if (handler == optionHandlers.end()) 74 | throw CommandLineParserException(std::string("Invalid option ") + optionName); 75 | 76 | handler->second(); 77 | } 78 | 79 | return config; 80 | } 81 | 82 | private: 83 | template 84 | std::function makeOptionHandler(const std::string& optionName, T Config::* field) { 85 | return [this, optionName, field]() { 86 | ++argsIterator; 87 | if (argsIterator == args.end()) 88 | throw CommandLineParserException(std::string("Option value required for ") + optionName); 89 | config.*field = detail::parseValue(*argsIterator); 90 | }; 91 | } 92 | 93 | std::function makeOptionHandler(const std::string& /*optionName*/, bool Config::* field) { 94 | return [this, field]() { 95 | config.*field = true; 96 | }; 97 | } 98 | 99 | void validateOptionName(const std::string& optionName) { 100 | if (optionHandlers.count(optionName)) 101 | throw CommandLineParserException(std::string("Duplicate option definition ") + optionName); 102 | 103 | if (optionName.empty() || optionName[0] != '-') 104 | throw CommandLineParserException(std::string("Option name must begin with '-': ") + optionName); 105 | } 106 | 107 | Config config; 108 | 109 | std::vector args; 110 | std::vector::const_iterator argsIterator; 111 | std::map> optionHandlers; 112 | }; 113 | 114 | } // namespace cmdparse 115 | 116 | -------------------------------------------------------------------------------- /doc/demo/main.cpp: -------------------------------------------------------------------------------- 1 | #include "ascii_graphics.h" 2 | #include "cmdparse.h" 3 | #include 4 | 5 | struct Options { 6 | std::string name; 7 | int numExclamations; 8 | #ifdef OPT_FRAME 9 | bool drawFrame; 10 | #endif 11 | 12 | Options() 13 | : name("World") 14 | , numExclamations(1) 15 | #ifdef OPT_FRAME 16 | , drawFrame(false) 17 | #endif 18 | {} 19 | }; 20 | 21 | int main(int argc, const char* argv[]) { 22 | Options options; 23 | cmdparse::CommandLineParser parser; 24 | 25 | try { 26 | parser.add("-name", &Options::name) 27 | #ifdef OPT_FRAME 28 | .add("-f", &Options::drawFrame) 29 | #endif 30 | .add("-c", &Options::numExclamations); 31 | 32 | options = parser.parse(argv + 1, argv + argc); 33 | } catch (const std::exception& e) { 34 | std::cerr << e.what() << std::endl; 35 | return 1; 36 | } 37 | 38 | const std::string greeting = std::string("Hello, ") + options.name 39 | + std::string(options.numExclamations, '!'); 40 | 41 | #ifdef OPT_FRAME 42 | if (options.drawFrame) 43 | ascii::outputInFrame(greeting, std::cout); 44 | else 45 | std::cout << greeting << std::endl; 46 | #else 47 | std::cout << greeting << std::endl; 48 | #endif 49 | 50 | return 0; 51 | } 52 | 53 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.12) 2 | if(POLICY CMP0048) 3 | cmake_policy(SET CMP0048 NEW) 4 | endif() 5 | if(POLICY CMP0074) 6 | cmake_policy(SET CMP0074 NEW) 7 | endif() 8 | 9 | project(CaideInliner) 10 | 11 | option(CAIDE_USE_SYSTEM_CLANG "Use system-installed clang/llvm instead of compiling them from scratch" OFF) 12 | 13 | # https://llvm.org/docs/CMake.html#embedding-llvm-in-your-project 14 | 15 | if(CAIDE_USE_SYSTEM_CLANG) 16 | # Clang CMake files don't define Clang_PACKAGE_VERSION. To use a specific version, 17 | # run cmake with '-DClang_ROOT=/path/to/clang/cmake/files (typically /usr/lib/llvm-{version}). 18 | find_package(Clang REQUIRED CONFIG) 19 | message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") 20 | message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") 21 | else() 22 | set(LLVM_EXTERNAL_CLANG_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/llvm-project/clang" CACHE PATH "" FORCE) 23 | set(LLVM_ENABLE_PROJECTS clang) 24 | set(LLVM_TARGETS_TO_BUILD "X86" CACHE STRING "" FORCE) 25 | option(BUILD_SHARED_LIBS "" OFF) 26 | option(LLVM_BUILD_TOOLS "" ON) 27 | option(LLVM_BUILD_EXAMPLES "" OFF) 28 | option(LLVM_BUILD_TESTS "" OFF) 29 | option(LLVM_BUILD_DOCS "" OFF) 30 | option(LLVM_ENABLE_DOXYGEN "" OFF) 31 | option(LLVM_ENABLE_SPHINX "" OFF) 32 | option(LLVM_ENABLE_TERMINFO "" OFF) 33 | option(LLVM_ENABLE_LIBEDIT "" OFF) 34 | option(LLVM_ENABLE_LIBPFM "" OFF) 35 | option(LLVM_ENABLE_LIBXML2 "" OFF) 36 | option(LLVM_ENABLE_OCAMLDOC "" OFF) 37 | option(LLVM_ENABLE_PLUGINS "" OFF) 38 | option(LLVM_ENABLE_RTTI "" ON) 39 | option(LLVM_ENABLE_ZLIB "" OFF) 40 | option(LLVM_ENABLE_ZSTD "" OFF) 41 | option(LLVM_INCLUDE_BENCHMARKS "" OFF) 42 | option(LLVM_INCLUDE_DOCS "" OFF) 43 | option(LLVM_INCLUDE_EXAMPLES "" OFF) 44 | option(LLVM_INCLUDE_GOTESTS "" OFF) 45 | option(LLVM_INCLUDE_RUNTIMES "" OFF) 46 | option(LLVM_INCLUDE_TESTS "" OFF) 47 | option(LLVM_INCLUDE_TOOLS "" ON) 48 | option(LLVM_INCLUDE_UTILS "" OFF) 49 | option(WITH_POLLY "" OFF) 50 | set(HAVE_LIBEDIT false CACHE BOOL "" FORCE) 51 | 52 | add_subdirectory(llvm-project/llvm EXCLUDE_FROM_ALL) 53 | 54 | if(EXISTS "${CMAKE_CURRENT_BINARY_DIR}/lib/cmake/clang/ClangConfig.cmake") 55 | if(POLICY CMP0024) 56 | cmake_policy(SET CMP0024 OLD) 57 | endif() 58 | include("${CMAKE_CURRENT_BINARY_DIR}/lib/cmake/clang/ClangConfig.cmake") 59 | else() 60 | # If the above doesn't work, we hardcode certain variables: 61 | set(LLVM_PACKAGE_VERSION "20.1.1") 62 | set(LLVM_LIBRARY_DIRS "${CMAKE_CURRENT_BINARY_DIR}/llvm-project/llvm/lib") 63 | set(LLVM_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/llvm-project/llvm/include" 64 | "${CMAKE_CURRENT_BINARY_DIR}/llvm-project/llvm/include") 65 | set(CLANG_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/llvm-project/clang/include" 66 | "${CMAKE_CURRENT_BINARY_DIR}/llvm-project/llvm/tools/clang/include") 67 | set(LLVM_DEFINITIONS "") 68 | endif() 69 | 70 | message(STATUS "Using LLVM from submodule ${LLVM_PACKAGE_VERSION}") 71 | endif() 72 | 73 | if(LLVM_PACKAGE_VERSION VERSION_LESS "9") 74 | message(WARNING "A version of clang/llvm that is too old is detected. Build failure is possible.") 75 | endif() 76 | if(LLVM_PACKAGE_VERSION VERSION_GREATER_EQUAL "21") 77 | message(WARNING "A version of clang/llvm that is too recent is detected. Build failure is possible.") 78 | endif() 79 | 80 | option(CAIDE_LINK_LLVM_DYLIB 81 | "Link against the LLVM dynamic library" 82 | ${LLVM_LINK_LLVM_DYLIB}) 83 | 84 | option(CAIDE_LINK_CLANG_DYLIB 85 | "Link against the clang dynamic library" 86 | ${CLANG_LINK_CLANG_DYLIB}) 87 | 88 | message(STATUS "Link against the LLVM dynamic library: ${CAIDE_LINK_LLVM_DYLIB}") 89 | message(STATUS "Link against the Clang dynamic library: ${CAIDE_LINK_CLANG_DYLIB}") 90 | 91 | message(STATUS "LLVM_LIBRARY_DIRS=${LLVM_LIBRARY_DIRS}") 92 | message(STATUS "CLANG_LIBRARY_DIRS=${CLANG_LIBRARY_DIRS}") 93 | message(STATUS "LLVM_INCLUDE_DIRS=${LLVM_INCLUDE_DIRS}") 94 | message(STATUS "CLANG_INCLUDE_DIRS=${CLANG_INCLUDE_DIRS}") 95 | message(STATUS "LLVM_DEFINITIONS=${LLVM_DEFINITIONS}") 96 | message(STATUS "CLANG_DEFINITIONS=${CLANG_DEFINITIONS}") 97 | 98 | 99 | include(CheckCXXCompilerFlag) 100 | 101 | function(add_compiler_option_if_supported opt) 102 | CHECK_CXX_COMPILER_FLAG("${opt}" compiler_supports_option) 103 | if(compiler_supports_option) 104 | add_compile_options("${opt}") 105 | endif() 106 | endfunction(add_compiler_option_if_supported) 107 | 108 | # CXX_STANDARD() is available only in cmake >= 3. 109 | # Set the compiler flag explicitly. 110 | if(LLVM_PACKAGE_VERSION VERSION_LESS "10") 111 | add_compiler_option_if_supported("-std=c++11") 112 | elseif(LLVM_PACKAGE_VERSION VERSION_LESS "16") 113 | add_compiler_option_if_supported("-std=c++14") 114 | else() 115 | add_compiler_option_if_supported("-std=c++17") 116 | endif() 117 | 118 | if(MSVC) 119 | add_compile_options(/W3) 120 | foreach(flag /permissive-) 121 | add_compiler_option_if_supported("${flag}") 122 | endforeach() 123 | # Disable warnings in clang headers. See https://devblogs.microsoft.com/cppblog/broken-warnings-theory. 124 | CHECK_CXX_COMPILER_FLAG(/experimental:external compiler_supports_external_options) 125 | if(compiler_supports_external_options) 126 | add_compile_options(/experimental:external /external:anglebrackets /external:W0) 127 | endif() 128 | else() 129 | foreach(flag -Wall -Wextra -Wshadow -Wlogical-op -Werror=return-type) 130 | add_compiler_option_if_supported("${flag}") 131 | endforeach() 132 | endif() 133 | 134 | 135 | add_library(caideInliner STATIC 136 | caideInliner.cpp clang_compat.cpp detect_options.cpp DependenciesCollector.cpp inliner.cpp 137 | MergeNamespacesVisitor.cpp optimizer.cpp OptimizerVisitor.cpp RemoveInactivePreprocessorBlocks.cpp 138 | sema_utils.cpp SmartRewriter.cpp SourceInfo.cpp SourceLocationComparers.cpp util.cpp Timer.cpp) 139 | 140 | target_include_directories(caideInliner SYSTEM PRIVATE ${CLANG_INCLUDE_DIRS} ${LLVM_INCLUDE_DIRS}) 141 | target_compile_definitions(caideInliner PRIVATE ${CLANG_DEFINITIONS} ${LLVM_DEFINITIONS}) 142 | 143 | if(CAIDE_LINK_CLANG_DYLIB) 144 | set(CAIDE_INLINER_CLANG_LIBS clang-cpp) 145 | else(CAIDE_LINK_CLANG_DYLIB) 146 | set(CAIDE_INLINER_CLANG_LIBS clangAST 147 | clangBasic 148 | clangFrontend 149 | clangLex 150 | clangRewrite 151 | clangSema 152 | clangTooling) 153 | endif(CAIDE_LINK_CLANG_DYLIB) 154 | 155 | if(CAIDE_LINK_LLVM_DYLIB) 156 | set(CAIDE_INLINER_LLVM_LIBS LLVM) 157 | else(CAIDE_LINK_LLVM_DYLIB) 158 | set(CAIDE_INLINER_LLVM_LIBS ) 159 | endif(CAIDE_LINK_LLVM_DYLIB) 160 | 161 | target_link_libraries(caideInliner PRIVATE ${CAIDE_INLINER_CLANG_LIBS} ${CAIDE_INLINER_LLVM_LIBS}) 162 | 163 | add_subdirectory(cmd) 164 | 165 | enable_testing() 166 | add_subdirectory(test-tool) 167 | 168 | 169 | # Find all libraries that we depend on, including recursive dependencies. 170 | # The result is in topological order. 171 | # This is only needed for linking in external build tools. 172 | function(get_dependencies_recursively_impl target output_var entered_var) 173 | if(NOT TARGET ${target} OR NOT ${target} MATCHES "^(caide|clang|LLVM).*") 174 | return() 175 | endif() 176 | 177 | list(FIND ${entered_var} ${target} idx) 178 | if(${idx} GREATER "-1") 179 | return() 180 | endif() 181 | 182 | list(APPEND ${entered_var} ${target}) 183 | 184 | get_target_property(imported ${target} IMPORTED) 185 | if(imported) 186 | get_target_property(dependencies ${target} INTERFACE_LINK_LIBRARIES) 187 | else() 188 | get_target_property(dependencies ${target} LINK_LIBRARIES) 189 | endif() 190 | 191 | if(dependencies) 192 | # message(STATUS "${target} -> ${dependencies}") 193 | foreach(dep ${dependencies}) 194 | get_dependencies_recursively_impl(${dep} ${output_var} ${entered_var}) 195 | endforeach() 196 | endif() 197 | 198 | list(APPEND ${output_var} ${target}) 199 | 200 | set(${entered_var} ${${entered_var}} PARENT_SCOPE) 201 | set(${output_var} ${${output_var}} PARENT_SCOPE) 202 | endfunction() 203 | 204 | function(get_dependencies_recursively target output_var) 205 | set(${output_var} "" PARENT_SCOPE) 206 | set(entered "") 207 | get_dependencies_recursively_impl(${target} ${output_var} entered) 208 | list(REVERSE ${output_var}) 209 | set(${output_var} ${${output_var}} PARENT_SCOPE) 210 | endfunction() 211 | 212 | get_dependencies_recursively(caideInliner all_libs) 213 | message(STATUS "All libs: ${all_libs}") 214 | file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/caide-libs.txt" "${all_libs}") 215 | unset(all_libs) 216 | 217 | -------------------------------------------------------------------------------- /src/DependenciesCollector.h: -------------------------------------------------------------------------------- 1 | // Caide C++ inliner 2 | // 3 | // This file is distributed under the GNU General Public License as published by 4 | // the Free Software Foundation, either version 3 of the License, or (at your 5 | // option) any later version. See LICENSE.TXT for details. 6 | 7 | #pragma once 8 | 9 | #include "clang_version.h" 10 | #include "SourceLocationComparers.h" 11 | 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | 22 | namespace clang { 23 | class Sema; 24 | class SourceManager; 25 | class TemplateArgumentList; 26 | } 27 | 28 | 29 | namespace caide { 30 | namespace internal { 31 | 32 | struct SourceInfo; 33 | struct SugaredSignature; 34 | 35 | 36 | class DependenciesCollector: public clang::RecursiveASTVisitor { 37 | public: 38 | DependenciesCollector(clang::SourceManager& srcMgr, 39 | clang::Sema& sema, 40 | const std::unordered_set& identifiersToKeep, 41 | SourceInfo& srcInfo_); 42 | 43 | bool shouldVisitImplicitCode() const; 44 | bool shouldVisitTemplateInstantiations() const; 45 | bool shouldWalkTypesOfTypeLocs() const; 46 | 47 | bool TraverseDecl(clang::Decl*); 48 | bool TraverseTemplateSpecializationType(clang::TemplateSpecializationType*); 49 | bool TraverseTemplateSpecializationTypeLoc(clang::TemplateSpecializationTypeLoc); 50 | 51 | bool TraverseCallExpr(clang::CallExpr*); 52 | 53 | bool VisitType(clang::Type*); 54 | bool VisitTypedefType(clang::TypedefType*); 55 | bool VisitTemplateSpecializationType(clang::TemplateSpecializationType*); 56 | #if CAIDE_CLANG_VERSION_AT_LEAST(10,0) 57 | bool TraverseAutoType(clang::AutoType*); 58 | bool TraverseVarDecl(clang::VarDecl*); 59 | bool VisitAutoType(clang::AutoType*); 60 | #endif 61 | 62 | bool VisitStmt(clang::Stmt* stmt); 63 | 64 | bool VisitDecl(clang::Decl* decl); 65 | bool VisitNamedDecl(clang::NamedDecl* namedDecl); 66 | bool VisitCallExpr(clang::CallExpr* callExpr); 67 | bool VisitCXXConstructExpr(clang::CXXConstructExpr* constructorExpr); 68 | bool VisitCXXConstructorDecl(clang::CXXConstructorDecl* ctorDecl); 69 | bool VisitTemplateTypeParmDecl(clang::TemplateTypeParmDecl* paramDecl); 70 | bool VisitDeclRefExpr(clang::DeclRefExpr* ref); 71 | bool VisitValueDecl(clang::ValueDecl* valueDecl); 72 | bool VisitVarDecl(clang::VarDecl*); 73 | bool VisitMemberExpr(clang::MemberExpr* memberExpr); 74 | bool VisitLambdaExpr(clang::LambdaExpr* lambdaExpr); 75 | bool VisitFieldDecl(clang::FieldDecl* field); 76 | bool VisitTypeAliasDecl(clang::TypeAliasDecl* aliasDecl); 77 | bool VisitTypeAliasTemplateDecl(clang::TypeAliasTemplateDecl* aliasTemplateDecl); 78 | bool VisitClassTemplateDecl(clang::ClassTemplateDecl* templateDecl); 79 | bool VisitClassTemplateSpecializationDecl(clang::ClassTemplateSpecializationDecl* specDecl); 80 | bool VisitVarTemplateSpecializationDecl(clang::VarTemplateSpecializationDecl* specDecl); 81 | bool TraverseClassTemplateSpecializationDecl(clang::ClassTemplateSpecializationDecl*); 82 | bool VisitFunctionDecl(clang::FunctionDecl* f); 83 | bool VisitFunctionTemplateDecl(clang::FunctionTemplateDecl* functionTemplate); 84 | bool VisitCXXMethodDecl(clang::CXXMethodDecl* method); 85 | bool VisitCXXRecordDecl(clang::CXXRecordDecl* recordDecl); 86 | bool VisitUsingShadowDecl(clang::UsingShadowDecl* usingDecl); 87 | bool VisitEnumDecl(clang::EnumDecl* enumDecl); 88 | #if CAIDE_CLANG_VERSION_AT_LEAST(10,0) 89 | bool TraverseConceptSpecializationExpr(clang::ConceptSpecializationExpr* conceptExpr); 90 | bool VisitConceptSpecializationExpr(clang::ConceptSpecializationExpr* conceptExpr); 91 | #endif 92 | 93 | void printGraph(std::ostream& out) const; 94 | 95 | private: 96 | clang::Decl* getCurrentDecl() const; 97 | clang::FunctionDecl* getCurrentFunction(clang::Decl* decl) const; 98 | clang::Decl* getParentDecl(clang::Decl* decl) const; 99 | 100 | clang::Decl* getCorrespondingDeclInNonInstantiatedContext(clang::Decl* semanticDecl) const; 101 | 102 | void traverseTemplateSpecializationTypeImpl( 103 | const clang::TemplateSpecializationType*, 104 | bool traverseTypeLocs); 105 | void traverseSugaredSignature(const SugaredSignature&, bool traverseTypeLocs = true); 106 | 107 | void insertReference(clang::Decl* from, clang::Decl* to); 108 | 109 | 110 | clang::SourceManager& sourceManager; 111 | clang::Sema& sema; 112 | const std::unordered_set& identifiersToKeep; 113 | SourceInfo& srcInfo; 114 | 115 | // There is no getParentDecl(stmt) function, so we maintain the stack of Decls, 116 | // with inner-most active Decl at the top of the stack. 117 | // \sa TraverseDecl(). 118 | std::stack declStack; 119 | }; 120 | 121 | } 122 | } 123 | 124 | -------------------------------------------------------------------------------- /src/IntervalSet.h: -------------------------------------------------------------------------------- 1 | // Caide C++ inliner 2 | // 3 | // This file is distributed under the GNU General Public License as published by 4 | // the Free Software Foundation, either version 3 of the License, or (at your 5 | // option) any later version. See LICENSE.TXT for details. 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | 12 | namespace caide { 13 | namespace internal { 14 | 15 | 16 | template> 17 | class IntervalSet { 18 | private: 19 | using IntervalMap = std::map; 20 | 21 | public: 22 | using const_iterator = typename IntervalMap::const_iterator; 23 | 24 | explicit IntervalSet(const Compare& comp = Compare()) 25 | : isLess(comp) 26 | , intervals(isLess) 27 | {} 28 | 29 | const_iterator begin() const { 30 | return intervals.begin(); 31 | } 32 | 33 | const_iterator end() const { 34 | return intervals.end(); 35 | } 36 | 37 | /// Add an interval [left, right], both ends inclusive. 38 | /// Assumes left <= right. 39 | void add(const Key& left, const Key& right) { 40 | auto it = intervals.upper_bound(left); 41 | auto prev = it; 42 | bool leftIsCovered = true; 43 | if (it == intervals.begin()) 44 | leftIsCovered = false; 45 | else { 46 | --prev; 47 | // prev is the last interval with prev->first <= left. 48 | leftIsCovered = !isLess(prev->second, left); 49 | } 50 | 51 | if (leftIsCovered) { 52 | // left is covered by prev. 53 | it = prev; 54 | if (isLess(it->second, right)) { 55 | // Extend the interval. 56 | it->second = std::move(right); 57 | } else { 58 | // right is covered too - no changes. 59 | return; 60 | } 61 | } else 62 | it = intervals.emplace_hint(it, left, right); 63 | 64 | // At this point, all intervals to the left of it don't intersect each other or 65 | // intervals to the right of (and including) it. Some intervals following it may intersect 66 | // it, so we have to merge them (by extending the right end of it). 67 | auto jt = intervals.upper_bound(it->second); 68 | auto lastIntersecting = std::prev(jt); 69 | // Optimization: no need to call comparison function with equal intervals. 70 | if (lastIntersecting != it && isLess(it->second, lastIntersecting->second)) 71 | it->second = std::move(lastIntersecting->second); 72 | 73 | // Intervals in (it, jt) (both non-inclusive) are now completely inside of it. Erase them. 74 | intervals.erase(std::next(it), jt); 75 | } 76 | 77 | /// Does any interval of the set intersect [left, right] (both ends inclusive)? 78 | /// Assumes left <= right. 79 | bool intersects(const Key& left, const Key& right) const { 80 | auto it = intervals.upper_bound(right); 81 | // If [left, right] doesn't intersect the set, it must be completely between prev(it) and it. 82 | 83 | if (it == intervals.begin()) 84 | return false; 85 | --it; 86 | 87 | if (!isLess(it->second, left)) 88 | return true; 89 | return false; 90 | } 91 | 92 | private: 93 | Compare isLess; 94 | 95 | /// Stores pairwise non-intersecting intervals, in increasing order. Keys are left ends 96 | /// of the intervals, values are corresponding right ends. 97 | IntervalMap intervals; 98 | }; 99 | 100 | } 101 | } 102 | 103 | -------------------------------------------------------------------------------- /src/MergeNamespacesVisitor.cpp: -------------------------------------------------------------------------------- 1 | // Caide C++ inliner 2 | // 3 | // This file is distributed under the GNU General Public License as published by 4 | // the Free Software Foundation, either version 3 of the License, or (at your 5 | // option) any later version. See LICENSE.TXT for details. 6 | 7 | #include "MergeNamespacesVisitor.h" 8 | #include "clang_compat.h" 9 | #include "SmartRewriter.h" 10 | #include "util.h" 11 | 12 | #include 13 | #include 14 | 15 | using namespace clang; 16 | 17 | 18 | namespace caide { 19 | namespace internal { 20 | 21 | 22 | MergeNamespacesVisitor::MergeNamespacesVisitor(SourceManager& sourceManager_, 23 | const std::unordered_set& removed_, SmartRewriter& rewriter_) 24 | : sourceManager(sourceManager_) 25 | , removed(removed_) 26 | , rewriter(rewriter_) 27 | {} 28 | 29 | bool MergeNamespacesVisitor::shouldVisitImplicitCode() const { return false; } 30 | bool MergeNamespacesVisitor::shouldVisitTemplateInstantiations() const { return false; } 31 | 32 | 33 | bool MergeNamespacesVisitor::TraverseDecl(Decl* decl) { 34 | bool ret = RecursiveASTVisitor::TraverseDecl(decl); 35 | 36 | if (decl && sourceManager.isInMainFile(getBeginLoc(decl)) && removed.count(decl) == 0) { 37 | if (auto* nsDecl = dyn_cast(decl)) 38 | closedNamespaces.push(nsDecl); 39 | else { 40 | auto* parentContext = decl->getLexicalDeclContext(); 41 | // Note: a type alias is not a declaration context, so the lexical context of its template 42 | // arguments is the enclosing namespace/class/function etc. It means that this check may give a 43 | // false positive. So we skip TemplateTypeParmDecl (it's always attached to another Decl anyway). 44 | // We also skip non-top-level Decls (they might have been removed as part of a parent Decl). 45 | if (!isa(decl) && 46 | (isa(parentContext) || isa(parentContext))) 47 | { 48 | // A non-removed declaration interrupts the chain of closed namespaces 49 | closedNamespaces = std::stack{}; 50 | } 51 | } 52 | } 53 | 54 | return ret; 55 | } 56 | 57 | bool MergeNamespacesVisitor::VisitNamespaceDecl(NamespaceDecl* nsDecl) { 58 | if (!sourceManager.isInMainFile(getBeginLoc(nsDecl))) 59 | return true; 60 | 61 | if (nsDecl && removed.count(nsDecl) == 0) { 62 | NamespaceDecl* canonicalDecl = nsDecl->getCanonicalDecl(); 63 | if (!closedNamespaces.empty() && canonicalDecl == closedNamespaces.top()->getCanonicalDecl()) { 64 | // Merge with previous namespace. 65 | SourceLocation closingBraceLoc = closedNamespaces.top()->getRBraceLoc(); 66 | rewriter.removeRange(closingBraceLoc, closingBraceLoc); 67 | closedNamespaces.pop(); 68 | 69 | ASTContext& astContext = nsDecl->getASTContext(); 70 | SourceLocation thisNamespaceNameStart = 71 | findTokenAfterLocation(getBeginLoc(nsDecl), astContext, tok::raw_identifier); 72 | SourceLocation thisNamespaceOpeningBrace = 73 | findTokenAfterLocation(thisNamespaceNameStart, astContext, tok::l_brace); 74 | 75 | rewriter.removeRange(getBeginLoc(nsDecl), thisNamespaceOpeningBrace); 76 | } 77 | } 78 | 79 | return true; 80 | } 81 | 82 | 83 | } 84 | } 85 | 86 | -------------------------------------------------------------------------------- /src/MergeNamespacesVisitor.h: -------------------------------------------------------------------------------- 1 | // Caide C++ inliner 2 | // 3 | // This file is distributed under the GNU General Public License as published by 4 | // the Free Software Foundation, either version 3 of the License, or (at your 5 | // option) any later version. See LICENSE.TXT for details. 6 | 7 | #pragma once 8 | 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | 15 | namespace clang { 16 | class SourceManager; 17 | } 18 | 19 | namespace caide { 20 | namespace internal { 21 | 22 | 23 | class SmartRewriter; 24 | 25 | 26 | class MergeNamespacesVisitor: public clang::RecursiveASTVisitor { 27 | public: 28 | MergeNamespacesVisitor(clang::SourceManager& sourceManager, 29 | const std::unordered_set& removed_, SmartRewriter& rewriter_); 30 | 31 | bool shouldVisitImplicitCode() const; 32 | bool shouldVisitTemplateInstantiations() const; 33 | 34 | bool TraverseDecl(clang::Decl* decl); 35 | bool VisitNamespaceDecl(clang::NamespaceDecl* namespaceDecl); 36 | 37 | private: 38 | // The stack of non-empty lexical namespaces that were closed most recently 'in a row' (without 39 | // non-removed declarations between closing braces). 40 | std::stack closedNamespaces; 41 | 42 | clang::SourceManager& sourceManager; 43 | // Removed lexical declarations. 44 | const std::unordered_set& removed; 45 | SmartRewriter& rewriter; 46 | }; 47 | 48 | } 49 | } 50 | 51 | -------------------------------------------------------------------------------- /src/OptimizerVisitor.h: -------------------------------------------------------------------------------- 1 | // Caide C++ inliner 2 | // 3 | // This file is distributed under the GNU General Public License as published by 4 | // the Free Software Foundation, either version 3 of the License, or (at your 5 | // option) any later version. See LICENSE.TXT for details. 6 | 7 | #pragma once 8 | 9 | #include "clang_version.h" 10 | 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | 21 | namespace clang { 22 | class SourceManager; 23 | class ASTContext; 24 | } 25 | 26 | namespace caide { 27 | namespace internal { 28 | 29 | 30 | class SmartRewriter; 31 | 32 | 33 | class OptimizerVisitor: public clang::RecursiveASTVisitor { 34 | public: 35 | OptimizerVisitor(clang::SourceManager& srcManager, const std::unordered_set& usedDecls, 36 | std::unordered_set& removedDecls, SmartRewriter& rewriter_); 37 | 38 | bool shouldVisitImplicitCode() const; 39 | bool shouldVisitTemplateInstantiations() const; 40 | 41 | bool TraverseDecl(clang::Decl* decl); 42 | 43 | bool VisitEmptyDecl(clang::EmptyDecl* decl); 44 | bool VisitNamespaceDecl(clang::NamespaceDecl* namespaceDecl); 45 | bool VisitFunctionDecl(clang::FunctionDecl* functionDecl); 46 | bool VisitFunctionTemplateDecl(clang::FunctionTemplateDecl* templateDecl); 47 | bool VisitCXXRecordDecl(clang::CXXRecordDecl* recordDecl); 48 | bool VisitClassTemplateDecl(clang::ClassTemplateDecl* templateDecl); 49 | bool VisitTypedefDecl(clang::TypedefDecl* typedefDecl); 50 | bool VisitTypeAliasDecl(clang::TypeAliasDecl* aliasDecl); 51 | bool VisitTypeAliasTemplateDecl(clang::TypeAliasTemplateDecl* aliasDecl); 52 | bool VisitUsingDirectiveDecl(clang::UsingDirectiveDecl* usingDecl); 53 | bool VisitVarDecl(clang::VarDecl* varDecl); 54 | bool VisitVarTemplateDecl(clang::VarTemplateDecl*); 55 | bool VisitEnumDecl(clang::EnumDecl* enumDecl); 56 | bool VisitFriendDecl(clang::FriendDecl* friendDecl); 57 | bool VisitFieldDecl(clang::FieldDecl* fieldDecl); 58 | bool VisitStaticAssertDecl(clang::StaticAssertDecl* staticAssertDecl); 59 | #if CAIDE_CLANG_VERSION_AT_LEAST(10,0) 60 | bool VisitConceptDecl(clang::ConceptDecl* D); 61 | #endif 62 | 63 | // Apply changes that require some 'global' knowledge. 64 | // Called after traversal of the whole AST. 65 | void Finalize(clang::ASTContext& ctx); 66 | 67 | private: 68 | bool needToRemoveFunction(clang::FunctionDecl* functionDecl) const; 69 | void removeDecl(clang::Decl* decl); 70 | 71 | // Process 'using namespace ns;' or 'using ns::identifier;' declaration 72 | // canonicalDecl is the namespace or the identifier that is the target of the using declaration 73 | // declContext is the declaration context where the using declaration is issued. 74 | // Return value is true when this using declaration is redundant. 75 | bool processUsingDirective(clang::Decl* canonicalDecl, clang::DeclContext* declContext); 76 | 77 | 78 | clang::SourceManager& sourceManager; 79 | const std::unordered_set& usedDeclarations; 80 | SmartRewriter& rewriter; 81 | 82 | std::unordered_set declared; 83 | std::unordered_set& removed; 84 | 85 | // Parent namespaces of non-removed Decls 86 | std::unordered_set nonEmptyLexicalNamespaces; 87 | 88 | // For each semantic declaraction context, keep track of which namespaces have been seen in 89 | // 'using namespace ns;' directives in this declaraction context (TODO: and which identifiers 90 | // have been seen in 'using ns::identifier' declaractions). 91 | std::unordered_map> seenInUsingDirectives; 92 | 93 | // Declarations of fields and static variables, grouped by their start location 94 | // (so comma separated declarations go into the same group). 95 | std::map> variables; 96 | }; 97 | 98 | 99 | } 100 | } 101 | 102 | -------------------------------------------------------------------------------- /src/RemoveInactivePreprocessorBlocks.h: -------------------------------------------------------------------------------- 1 | // Caide C++ inliner 2 | // 3 | // This file is distributed under the GNU General Public License as published by 4 | // the Free Software Foundation, either version 3 of the License, or (at your 5 | // option) any later version. See LICENSE.TXT for details. 6 | 7 | #pragma once 8 | 9 | #include "clang_version.h" 10 | 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | namespace clang { 19 | class LangOptions; 20 | class SourceManager; 21 | class MacroDirective; 22 | } 23 | 24 | namespace caide { 25 | namespace internal { 26 | 27 | class SmartRewriter; 28 | 29 | class RemoveInactivePreprocessorBlocks: public clang::PPCallbacks { 30 | private: 31 | struct RemoveInactivePreprocessorBlocksImpl; 32 | std::unique_ptr impl; 33 | 34 | public: 35 | RemoveInactivePreprocessorBlocks(clang::SourceManager& sourceManager_, const clang::LangOptions& langOptions, 36 | SmartRewriter& rewriter_, const std::set& macrosToKeep_); 37 | ~RemoveInactivePreprocessorBlocks(); 38 | 39 | void MacroDefined(const clang::Token& MacroNameTok, const clang::MacroDirective* MD) override; 40 | 41 | #if CAIDE_CLANG_VERSION_AT_LEAST(5,0) 42 | void MacroUndefined(const clang::Token& MacroNameTok, const clang::MacroDefinition& MD, const clang::MacroDirective* Undef) override; 43 | #elif CAIDE_CLANG_VERSION_AT_LEAST(3,7) 44 | void MacroUndefined(const clang::Token& MacroNameTok, const clang::MacroDefinition& MD) override; 45 | #else 46 | void MacroUndefined(const clang::Token& MacroNameTok, const clang::MacroDirective* MD) override; 47 | #endif 48 | 49 | #if CAIDE_CLANG_VERSION_AT_LEAST(3,7) 50 | void MacroExpands(const clang::Token& /*MacroNameTok*/, const clang::MacroDefinition& MD, 51 | clang::SourceRange Range, const clang::MacroArgs* /*Args*/) override; 52 | #else 53 | void MacroExpands(const clang::Token& /*MacroNameTok*/, const clang::MacroDirective* MD, 54 | clang::SourceRange Range, const clang::MacroArgs* /*Args*/) override; 55 | #endif 56 | 57 | void If(clang::SourceLocation Loc, clang::SourceRange ConditionRange, ConditionValueKind ConditionValue) override; 58 | #if CAIDE_CLANG_VERSION_AT_LEAST(3,7) 59 | void Ifdef(clang::SourceLocation Loc, const clang::Token& MacroNameTok, const clang::MacroDefinition& /*MD*/) override; 60 | void Ifndef(clang::SourceLocation Loc, const clang::Token& MacroNameTok, const clang::MacroDefinition& /*MD*/) override; 61 | #else 62 | void Ifdef(clang::SourceLocation Loc, const clang::Token& MacroNameTok, const clang::MacroDirective* /*MD*/) override; 63 | void Ifndef(clang::SourceLocation Loc, const clang::Token& MacroNameTok, const clang::MacroDirective* /*MD*/) override; 64 | #endif 65 | 66 | void Elif(clang::SourceLocation Loc, clang::SourceRange ConditionRange, ConditionValueKind ConditionValue, clang::SourceLocation /*IfLoc*/ ) override; 67 | void Else(clang::SourceLocation Loc, clang::SourceLocation /*IfLoc*/) override; 68 | void Endif(clang::SourceLocation Loc, clang::SourceLocation /*IfLoc*/) override; 69 | 70 | void InclusionDirective(clang::SourceLocation HashLoc, 71 | const clang::Token& IncludeTok, 72 | llvm::StringRef FileName, 73 | bool IsAngled, 74 | clang::CharSourceRange FilenameRange, 75 | #if CAIDE_CLANG_VERSION_AT_LEAST(16, 0) 76 | clang::OptionalFileEntryRef File, 77 | #elif CAIDE_CLANG_VERSION_AT_LEAST(15, 0) 78 | llvm::Optional File, 79 | #else 80 | const clang::FileEntry *File, 81 | #endif 82 | llvm::StringRef SearchPath, 83 | llvm::StringRef RelativePath, 84 | const clang::Module* /*SuggestedModule*/ 85 | #if CAIDE_CLANG_VERSION_AT_LEAST(19, 0) 86 | , bool /*ModuleImported*/ 87 | #endif 88 | #if CAIDE_CLANG_VERSION_AT_LEAST(7, 0) 89 | , clang::SrcMgr::CharacteristicKind FileType 90 | #endif 91 | ) override; 92 | 93 | // EndOfMainFile() is called too late; instead, we call this one manually in the consumer. 94 | void Finalize(); 95 | }; 96 | 97 | } 98 | } 99 | 100 | -------------------------------------------------------------------------------- /src/SmartRewriter.cpp: -------------------------------------------------------------------------------- 1 | // Caide C++ inliner 2 | // 3 | // This file is distributed under the GNU General Public License as published by 4 | // the Free Software Foundation, either version 3 of the License, or (at your 5 | // option) any later version. See LICENSE.TXT for details. 6 | 7 | #include "SmartRewriter.h" 8 | #include "SourceLocationComparers.h" 9 | 10 | #include 11 | 12 | #include 13 | 14 | using namespace clang; 15 | 16 | namespace caide { 17 | namespace internal { 18 | 19 | SmartRewriter::SmartRewriter(SourceManager& srcManager, const LangOptions& langOptions) 20 | : rewriter(srcManager, langOptions) 21 | , comparer(srcManager) 22 | , removed(comparer) 23 | , changesApplied(false) 24 | { 25 | } 26 | 27 | void SmartRewriter::appendToPreamble(std::string s) { 28 | preamble += std::move(s); 29 | } 30 | 31 | void SmartRewriter::removeRange(SourceLocation begin, SourceLocation end) { 32 | removed.add(begin, end); 33 | } 34 | 35 | void SmartRewriter::removeRange(const SourceRange& range) { 36 | removeRange(range.getBegin(), range.getEnd()); 37 | } 38 | 39 | bool SmartRewriter::isPartOfRangeRemoved(const SourceRange& range) const { 40 | return removed.intersects(range.getBegin(), range.getEnd()); 41 | } 42 | 43 | bool SmartRewriter::getRewriteBufferFor(FileID fileID, std::string& rewriteBuf) const { 44 | const auto* rewriteBuffer = rewriter.getRewriteBufferFor(fileID); 45 | if (rewriteBuffer == nullptr) { 46 | return false; 47 | } 48 | rewriteBuf.assign(rewriteBuffer->begin(), rewriteBuffer->end()); 49 | return true; 50 | } 51 | 52 | void SmartRewriter::applyChanges() { 53 | if (changesApplied) 54 | throw std::logic_error("Rewriter changes have already been applied"); 55 | changesApplied = true; 56 | 57 | Rewriter::RewriteOptions opts; 58 | for (const auto& range : removed) 59 | rewriter.RemoveText(SourceRange(range.first, range.second), opts); 60 | 61 | SourceManager& srcManager = rewriter.getSourceMgr(); 62 | SourceLocation Loc = srcManager.getLocForStartOfFile(srcManager.getMainFileID()); 63 | rewriter.InsertText(Loc, preamble); 64 | } 65 | 66 | } 67 | } 68 | 69 | -------------------------------------------------------------------------------- /src/SmartRewriter.h: -------------------------------------------------------------------------------- 1 | // Caide C++ inliner 2 | // 3 | // This file is distributed under the GNU General Public License as published by 4 | // the Free Software Foundation, either version 3 of the License, or (at your 5 | // option) any later version. See LICENSE.TXT for details. 6 | 7 | #pragma once 8 | 9 | #include "IntervalSet.h" 10 | #include "SourceLocationComparers.h" 11 | 12 | #include 13 | 14 | #include 15 | 16 | namespace clang { 17 | class LangOptions; 18 | class SourceManager; 19 | } 20 | 21 | namespace caide { 22 | namespace internal { 23 | 24 | class SmartRewriter { 25 | public: 26 | SmartRewriter(clang::SourceManager& sourceManager, const clang::LangOptions& langOptions); 27 | SmartRewriter(const SmartRewriter&) = delete; 28 | SmartRewriter& operator=(const SmartRewriter&) = delete; 29 | SmartRewriter(SmartRewriter&&) = delete; 30 | SmartRewriter& operator=(SmartRewriter&&) = delete; 31 | 32 | bool isPartOfRangeRemoved(const clang::SourceRange& range) const; 33 | void appendToPreamble(std::string s); 34 | void removeRange(clang::SourceLocation begin, clang::SourceLocation end); 35 | void removeRange(const clang::SourceRange& range); 36 | bool getRewriteBufferFor(clang::FileID fileID, std::string& rewriteBuf) const; 37 | void applyChanges(); 38 | 39 | private: 40 | clang::Rewriter rewriter; 41 | std::string preamble; 42 | SourceLocationComparer comparer; 43 | IntervalSet removed; 44 | bool changesApplied; 45 | }; 46 | 47 | } 48 | } 49 | 50 | -------------------------------------------------------------------------------- /src/SourceInfo.cpp: -------------------------------------------------------------------------------- 1 | // Caide C++ inliner 2 | // 3 | // This file is distributed under the GNU General Public License as published by 4 | // the Free Software Foundation, either version 3 of the License, or (at your 5 | // option) any later version. See LICENSE.TXT for details. 6 | 7 | #include "SourceInfo.h" 8 | 9 | namespace caide { 10 | namespace internal { 11 | 12 | std::pair SourceInfo::makeKey(clang::Decl* nonImplicitDecl) { 13 | // TODO: use getLocStart() instead of getLocation() ? 14 | return std::make_pair(nonImplicitDecl->getLocation(), nonImplicitDecl->getKind()); 15 | } 16 | 17 | } 18 | } 19 | 20 | -------------------------------------------------------------------------------- /src/SourceInfo.h: -------------------------------------------------------------------------------- 1 | // Caide C++ inliner 2 | // 3 | // This file is distributed under the GNU General Public License as published by 4 | // the Free Software Foundation, either version 3 of the License, or (at your 5 | // option) any later version. See LICENSE.TXT for details. 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | 18 | namespace clang { 19 | class Decl; 20 | class FunctionDecl; 21 | class VarDecl; 22 | } 23 | 24 | namespace caide { 25 | namespace internal { 26 | 27 | // Contains dependency graph and other information shared between optimizer stages. 28 | struct SourceInfo { 29 | // key: Decl, value: what the key uses. 30 | std::map> uses; 31 | 32 | // 'Roots of the dependency graph': 33 | // - int main() 34 | // - declarations marked with a comment '/// caide keep' 35 | // - declarations corresponding to names provided by identifiersToKeep setting. 36 | std::set declsToKeep; 37 | 38 | // Delayed parsed functions. 39 | std::vector delayedParsedFunctions; 40 | 41 | // value: non-implicit Decl, key: location and kind of the decl. 42 | std::map, clang::Decl*> nonImplicitDecls; 43 | 44 | static std::pair makeKey(clang::Decl* nonImplicitDecl); 45 | }; 46 | 47 | } 48 | } 49 | 50 | -------------------------------------------------------------------------------- /src/SourceLocationComparers.cpp: -------------------------------------------------------------------------------- 1 | // Caide C++ inliner 2 | // 3 | // This file is distributed under the GNU General Public License as published by 4 | // the Free Software Foundation, either version 3 of the License, or (at your 5 | // option) any later version. See LICENSE.TXT for details. 6 | 7 | #include "SourceLocationComparers.h" 8 | 9 | #include 10 | #include 11 | 12 | 13 | using clang::SourceLocation; 14 | using clang::SourceRange; 15 | 16 | 17 | namespace caide { 18 | namespace internal { 19 | 20 | SourceLocationComparer::SourceLocationComparer(const clang::SourceManager& sourceManager_) 21 | : sourceManager(sourceManager_) 22 | {} 23 | 24 | bool SourceLocationComparer::operator() (SourceLocation lhs, SourceLocation rhs) const { 25 | return sourceManager.isBeforeInTranslationUnit(lhs, rhs); 26 | } 27 | 28 | bool ArbitraryRangeComparer::operator() (const clang::SourceRange& lhs, const clang::SourceRange& rhs) const { 29 | clang::SourceLocation lbegin = lhs.getBegin(), rbegin = rhs.getBegin(); 30 | if (lbegin == rbegin) 31 | return lhs.getEnd() < rhs.getEnd(); 32 | else 33 | return lbegin < rbegin; 34 | } 35 | 36 | } 37 | } 38 | 39 | -------------------------------------------------------------------------------- /src/SourceLocationComparers.h: -------------------------------------------------------------------------------- 1 | // Caide C++ inliner 2 | // 3 | // This file is distributed under the GNU General Public License as published by 4 | // the Free Software Foundation, either version 3 of the License, or (at your 5 | // option) any later version. See LICENSE.TXT for details. 6 | 7 | #pragma once 8 | 9 | #include 10 | 11 | namespace clang { 12 | class SourceManager; 13 | class SourceRange; 14 | } 15 | 16 | namespace caide { 17 | namespace internal { 18 | 19 | // Unlike operator<, compares SourceLocations by their relative order in the source file. 20 | struct SourceLocationComparer { 21 | explicit SourceLocationComparer(const clang::SourceManager& sourceManager); 22 | SourceLocationComparer(const SourceLocationComparer&) = default; 23 | SourceLocationComparer(SourceLocationComparer&&) = default; 24 | 25 | bool operator() (clang::SourceLocation lhs, clang::SourceLocation rhs) const; 26 | const clang::SourceManager& sourceManager; 27 | }; 28 | 29 | // Compares SourceRanges in some arbitrary order. 30 | struct ArbitraryRangeComparer { 31 | bool operator() (const clang::SourceRange& lhs, const clang::SourceRange& rhs) const; 32 | }; 33 | 34 | } 35 | } 36 | 37 | -------------------------------------------------------------------------------- /src/Timer.cpp: -------------------------------------------------------------------------------- 1 | // Caide C++ inliner 2 | // 3 | // This file is distributed under the GNU General Public License as published by 4 | // the Free Software Foundation, either version 3 of the License, or (at your 5 | // option) any later version. See LICENSE.TXT for details. 6 | 7 | #include "Timer.h" 8 | 9 | // #define CAIDE_TIMER 10 | 11 | #ifdef CAIDE_TIMER 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | 18 | namespace caide { namespace internal { 19 | 20 | namespace { 21 | 22 | struct TimeReport { 23 | using Duration = std::chrono::steady_clock::duration; 24 | Duration duration; 25 | 26 | std::map children; 27 | }; 28 | 29 | struct ReportPrinter { 30 | TimeReport root; 31 | std::stack cur; 32 | static const int INDENT = 2; 33 | 34 | ReportPrinter() { cur.push(&root); } 35 | ~ReportPrinter() { print(root, "", -INDENT); } 36 | 37 | std::chrono::milliseconds print(const TimeReport& node, const std::string& name, int indent) { 38 | auto durationMs = std::chrono::duration_cast(node.duration); 39 | auto other = durationMs; 40 | if (indent >= 0) 41 | print(durationMs, name, indent); 42 | 43 | for (const auto& kv : node.children) 44 | other -= print(kv.second, kv.first, indent + INDENT); 45 | 46 | if (indent >= 0 && !node.children.empty() && other > std::chrono::milliseconds{0}) 47 | print(other, "Other", indent + INDENT); 48 | 49 | return durationMs; 50 | } 51 | 52 | void print(std::chrono::milliseconds duration, const std::string& name, int indent) { 53 | std::string durationStr = std::to_string(duration.count()); 54 | std::cerr << durationStr << " ms "; 55 | for (size_t i = durationStr.size(); i < (size_t)6; ++i) 56 | std::cerr << ' '; 57 | for (int i = 0; i < indent; ++i) 58 | std::cerr << ' '; 59 | std::cerr << name << '\n'; 60 | } 61 | } printer; 62 | 63 | } 64 | 65 | ScopedTimer::ScopedTimer(const std::string& name) { 66 | TimeReport* prev = printer.cur.top(); 67 | TimeReport& cur = prev->children[name]; 68 | printer.cur.push(&cur); 69 | duration = &cur.duration; 70 | resume(); 71 | } 72 | 73 | ScopedTimer::~ScopedTimer() { 74 | pause(); 75 | printer.cur.pop(); 76 | } 77 | 78 | void ScopedTimer::pause() { 79 | if (start != Clock::time_point::min()) { 80 | *duration += Clock::now() - start; 81 | start = Clock::time_point::min(); 82 | } 83 | } 84 | 85 | void ScopedTimer::resume() { 86 | start = Clock::now(); 87 | } 88 | 89 | }} 90 | 91 | #else 92 | 93 | namespace caide { namespace internal { 94 | 95 | ScopedTimer::ScopedTimer(const std::string&) { 96 | (void)start; 97 | (void)duration; 98 | } 99 | ScopedTimer::~ScopedTimer() = default; 100 | void ScopedTimer::pause() { } 101 | void ScopedTimer::resume() { } 102 | 103 | }} 104 | 105 | #endif 106 | 107 | -------------------------------------------------------------------------------- /src/Timer.h: -------------------------------------------------------------------------------- 1 | // Caide C++ inliner 2 | // 3 | // This file is distributed under the GNU General Public License as published by 4 | // the Free Software Foundation, either version 3 of the License, or (at your 5 | // option) any later version. See LICENSE.TXT for details. 6 | 7 | #include 8 | #include 9 | 10 | namespace caide { namespace internal { 11 | 12 | class ScopedTimer { 13 | public: 14 | ScopedTimer(const std::string& name); 15 | ~ScopedTimer(); 16 | 17 | void pause(); 18 | void resume(); 19 | 20 | private: 21 | using Clock = std::chrono::steady_clock; 22 | Clock::time_point start; 23 | Clock::duration* duration = nullptr; 24 | }; 25 | 26 | } } 27 | -------------------------------------------------------------------------------- /src/caideInliner.cpp: -------------------------------------------------------------------------------- 1 | // Caide C++ inliner 2 | // 3 | // This file is distributed under the GNU General Public License as published by 4 | // the Free Software Foundation, either version 3 of the License, or (at your 5 | // option) any later version. See LICENSE.TXT for details. 6 | 7 | #include "caideInliner.hpp" 8 | #include "caideInliner.h" 9 | 10 | #include "detect_options.h" 11 | #include "inliner.h" 12 | #include "optimizer.h" 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | 23 | using std::istringstream; 24 | using std::ofstream; 25 | using std::string; 26 | using std::vector; 27 | 28 | 29 | namespace caide { 30 | 31 | static string trimEndPathSeparators(const string& path) { 32 | string result{path}; 33 | auto lastSymbol = result.find_last_not_of("/\\"); 34 | if (lastSymbol != string::npos) 35 | result.erase(lastSymbol + 1); 36 | return result; 37 | } 38 | 39 | static bool startsWith(const string& s, const char* prefix) { 40 | return s.rfind(prefix, 0) == 0; 41 | } 42 | 43 | CppInliner::CppInliner(const string& temporaryDirectory_) 44 | : clangCompilationOptions{} 45 | , macrosToKeep{"__cplusplus", "__STDC_VERSION__", 46 | "__ANDROID__", "__MINGW32__", "__MINGW64__", 47 | "_WIN32", "_WIN64", "_M_AMD64", "__linux", "__linux__", "__APPLE__", 48 | "__GNUC__", "__GLIBC__", "__clang__", "_MSC_VER"} 49 | , maxConsequentEmptyLines{2} 50 | , temporaryDirectory{trimEndPathSeparators(temporaryDirectory_)} 51 | { 52 | } 53 | 54 | static void concatFiles(const vector& cppFilePaths, const string& outputFilePath) { 55 | ofstream out{outputFilePath}; 56 | for (const string& filePath : cppFilePaths) { 57 | std::ifstream in{filePath}; 58 | if (!in) 59 | throw std::runtime_error(string("File not found: " + filePath)); 60 | out << in.rdbuf(); 61 | out << '\n'; // in case there was no return at end of file 62 | } 63 | } 64 | 65 | // Certain directives become invalid after the first stage (inliner) runs. Those include: 66 | // * #pragma once (used to be in a header, now in the inlined source file). 67 | // * #line number [file name] (became incorrect due to inlining header files). 68 | // 69 | // Ideally, the inliner would not emit these directives. However, it may be hard to do 70 | // with currently available clang API. Instead, we use a simplistic postprocessing that 71 | // should work in most cases. 72 | static bool isInvalidDirective(string line) { 73 | auto it = std::remove_if(line.begin(), line.end(), 74 | [](char c) { return c == ' ' || c == '\t' || c == '\r'; }); 75 | line.erase(it, line.end()); 76 | // This is technically incorrect due to multiline directives and strings. 77 | return line == "#pragmaonce" || startsWith(line, "#line"); 78 | } 79 | 80 | static void removeInvalidDirectives(const string& textInBinaryMode, const string& outputFilePath) { 81 | istringstream in{textInBinaryMode}; 82 | ofstream out{outputFilePath, std::ios::binary}; 83 | string line; 84 | while (std::getline(in, line)) { 85 | if (!isInvalidDirective(line)) 86 | out << line << '\n'; 87 | } 88 | } 89 | 90 | static bool isWhitespaceOnly(const string& text) { 91 | return text.find_first_not_of(" \t\r") == string::npos; 92 | } 93 | 94 | static void removeEmptyLines(const string& textInBinaryMode, 95 | int maxConsequentEmptyLines, 96 | const string& outputFilePath) 97 | { 98 | if (maxConsequentEmptyLines < 0) 99 | maxConsequentEmptyLines = std::numeric_limits::max(); 100 | istringstream in{textInBinaryMode}; 101 | ofstream out{outputFilePath, std::ios::binary}; 102 | int currentConsequentEmptyLines = 0; 103 | bool readNonEmptyLine = false; 104 | string line; 105 | while (std::getline(in, line)) { 106 | if (isWhitespaceOnly(line)) 107 | ++currentConsequentEmptyLines; 108 | else { 109 | currentConsequentEmptyLines = 0; 110 | readNonEmptyLine = true; 111 | } 112 | 113 | if (readNonEmptyLine && currentConsequentEmptyLines <= maxConsequentEmptyLines) 114 | out << line << '\n'; 115 | } 116 | } 117 | 118 | static string pathConcat(const string& path, const string& fileName) { 119 | string result{path}; 120 | result.push_back('/'); 121 | result += fileName; 122 | return result; 123 | } 124 | 125 | void CppInliner::inlineCode(const vector& cppFilePaths, const string& outputFilePath) const { 126 | const string concatStage{pathConcat(temporaryDirectory, "concat.cpp")}; 127 | const string inlinedStage{pathConcat(temporaryDirectory, "inlined.cpp")}; 128 | 129 | concatFiles(cppFilePaths, concatStage); 130 | 131 | internal::Inliner inliner{clangCompilationOptions}; 132 | std::string inlinedCode{inliner.doInline(concatStage)}; 133 | removeInvalidDirectives(inlinedCode, inlinedStage); 134 | 135 | internal::Optimizer optimizer{inliner.getResultingCommandLineOptions(), macrosToKeep, identifiersToKeep}; 136 | std::string onlyReachableCode{optimizer.doOptimize(inlinedStage)}; 137 | removeEmptyLines(onlyReachableCode, maxConsequentEmptyLines, outputFilePath); 138 | } 139 | 140 | void CppInliner::autoDetectCompilationOptions() { 141 | clangCompilationOptions = internal::detectClangOptions(temporaryDirectory); 142 | } 143 | 144 | } // namespace caide 145 | 146 | static vector arrayToCppVector(const char** array, int size) { 147 | vector res(size); 148 | for (int i = 0; i < size; ++i) 149 | res[i].assign(array[i]); 150 | return res; 151 | } 152 | 153 | extern "C" int caideInlineCppCode( 154 | const CaideCppInlinerOptions* options, 155 | const char** cppFilePaths, 156 | int numCppFiles, 157 | const char* outputFilePath) 158 | { 159 | try { 160 | caide::CppInliner inliner(options->temporaryDirectory); 161 | inliner.clangCompilationOptions = arrayToCppVector( 162 | options->clangCompilationOptions, options->numClangOptions); 163 | inliner.macrosToKeep = arrayToCppVector(options->macrosToKeep, options->numMacrosToKeep); 164 | inliner.identifiersToKeep = arrayToCppVector(options->identifiersToKeep, options->numIdentifiersToKeep); 165 | inliner.maxConsequentEmptyLines = options->maxConsequentEmptyLines; 166 | vector files = arrayToCppVector(cppFilePaths, numCppFiles); 167 | inliner.inlineCode(files, outputFilePath); 168 | return 0; 169 | } catch (const std::exception& e) { 170 | return 1; 171 | } catch (...) { 172 | return 2; 173 | } 174 | } 175 | 176 | -------------------------------------------------------------------------------- /src/caideInliner.h: -------------------------------------------------------------------------------- 1 | /* Caide C++ inliner 2 | 3 | This file is distributed under the GNU General Public License as published by 4 | the Free Software Foundation, either version 3 of the License, or (at your 5 | option) any later version. See LICENSE.TXT for details. 6 | */ 7 | 8 | #pragma once 9 | 10 | /*! 11 | \file caideInliner.h 12 | \brief C interface for caide inliner API 13 | 14 | See documentation in caideInliner.hpp file for details. 15 | */ 16 | 17 | #ifdef __cplusplus 18 | extern "C" { 19 | #endif 20 | 21 | struct CaideCppInlinerOptions { 22 | const char* temporaryDirectory; 23 | 24 | const char** clangCompilationOptions; 25 | int numClangOptions; 26 | 27 | const char** macrosToKeep; 28 | int numMacrosToKeep; 29 | 30 | const char** identifiersToKeep; 31 | int numIdentifiersToKeep; 32 | 33 | int maxConsequentEmptyLines; 34 | }; 35 | 36 | int caideInlineCppCode( 37 | const struct CaideCppInlinerOptions* options, 38 | const char** cppFilePaths, 39 | int numCppFiles, 40 | const char* outputFilePath); 41 | 42 | #ifdef __cplusplus 43 | } /* extern "C" */ 44 | #endif 45 | -------------------------------------------------------------------------------- /src/caideInliner.hpp: -------------------------------------------------------------------------------- 1 | // Caide C++ inliner 2 | // 3 | // This file is distributed under the GNU General Public License as published by 4 | // the Free Software Foundation, either version 3 of the License, or (at your 5 | // option) any later version. See LICENSE.TXT for details. 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | 12 | /// \file caideInliner.hpp 13 | /// \brief C++ interface for caide inliner API 14 | 15 | 16 | namespace caide { 17 | 18 | /// \brief C++ code inliner and unused code remover 19 | /// 20 | /// The C++ inliner transforms a program implemented as multiple C++ source files 21 | /// and headers into a single-file program. Only code reachable from main function 22 | /// is kept in the output file. 23 | /// 24 | /// Fairly complex programs are supported, including programs using template metaprogramming 25 | /// and modern C++ features. That said, don't try to inline Boost headers. 26 | /// 27 | /// \sa inlineCode() 28 | class CppInliner { 29 | public: 30 | /// \brief Create an instance of C++ inliner 31 | /// \param temporaryDirectory path to a directory where intermediate 32 | /// files will be written. The directory must exist. 33 | /// 34 | /// \sa clangCompilationOptions 35 | /// \sa macrosToKeep 36 | /// \sa maxConsequentEmptyLines 37 | explicit CppInliner(const std::string& temporaryDirectory); 38 | 39 | 40 | /// \brief Generate a single-file C++ program equivalent to the multiple-file program. 41 | /// \param cppFilePaths full paths of all C++ files of a program 42 | /// \param outputFilePath path to a file where the inlined program will be written 43 | /// 44 | /// All input C++ files and included user headers will be combined into a single C++ file, 45 | /// and only code reachable from main function will be kept. In addition, declarations 46 | /// marked with a comment 'caide keep' will be kept too. 47 | void inlineCode(const std::vector& cppFilePaths, 48 | const std::string& outputFilePath) const; 49 | 50 | /// \brief Try to detect system include paths automatically and adjust 51 | /// clangCompilationOptions accordingly. 52 | /// 53 | /// \sa clangCompilationOptions 54 | void autoDetectCompilationOptions(); 55 | 56 | /// \brief clang compilation options (see http://clang.llvm.org/docs/CommandGuide/clang.html 57 | /// and http://clang.llvm.org/docs/UsersManual.html) 58 | /// 59 | /// Most commonly used options are: 60 | /// 61 | /// \li `-I` to setup include search paths 62 | /// \li `-isystem` to setup system include search paths. In particular, you will likely need 63 | /// to add builtin clang includes as a system path. See this link: 64 | /// http://clang.llvm.org/docs/LibTooling.html#libtooling-builtin-includes 65 | /// \li `-D` for custom symbol definitions 66 | /// \li `-std=c++11` to specify a version of C++ standard 67 | /// \li `-fparse-all-comments' to take into account non-documentation comments 68 | /// \li `-v` for verbose output 69 | /// 70 | /// Other options you may need if they are not determined automatically (e.g. if 71 | /// the library was built in MinGW but you want to use Visual Studio headers, or if 72 | /// the library was built in a different Linux distribution): 73 | /// 74 | /// \li `-target` to specify compilation target (e.g., i386-pc-mingw32, i386-pc-windows-msvc, 75 | /// i386-pc-linux) 76 | /// \li `-fmsc-version` to specify a version of Microsoft compiler installed in the system 77 | /// 78 | /// Default value is empty. 79 | std::vector clangCompilationOptions; 80 | 81 | 82 | /// \brief the list of macro definitions whose values should be assumed unknown 83 | /// 84 | /// The inliner will remove inactive preprocessor blocks. Sometimes it is 85 | /// not desirable. For example, if you have a Linux-specific code path 86 | /// and a Windows-specific code path, you want to always keep both of them. 87 | /// This parameter contains macro names, references to which in an 88 | /// #if or #ifdef condition will protect the corresponding preprocessor branches 89 | /// from being removed. 90 | /// 91 | /// Default value is a list of common OS-specific and compiler-specific definitions. 92 | /// 93 | /// \note The reference is checked by substring search. So, if this parameter 94 | /// contains a string "_WIN32" and a preprocessor condition is `#ifdef HAVE_WIN32` 95 | /// or `#if defined(_WIN32) || defined(_WIN64)`, then preprocessor branches 96 | /// following this condition will not be removed. 97 | /// 98 | /// \note Unused code inside an inactive preprocessor block is not removed. 99 | std::vector macrosToKeep; 100 | 101 | 102 | /// \brief limit on the number of consecutive empty lines in the output file 103 | /// 104 | /// When unused code is removed, it leaves behind large blocks of empty lines. 105 | /// This parameter controls the number of consequent empty lines that is kept 106 | /// in the output file. 107 | /// 108 | /// Default value is 2. If the parameter is negative, empty lines are not removed. 109 | int maxConsequentEmptyLines; 110 | 111 | /// \brief List of fully qualified names to preserve in the output, even if 112 | /// they are not reachable from main function 113 | /// 114 | /// Normally, only code reachable from main or marked by special comments 115 | /// is preserved. This settings provides additional identifiers to preserve. 116 | /// Identifiers must be fully qualified, e.g. "NamespaceName::ClassName::method". 117 | std::vector identifiersToKeep; 118 | 119 | private: 120 | const std::string temporaryDirectory; 121 | }; 122 | 123 | } // namespace caide 124 | 125 | -------------------------------------------------------------------------------- /src/caide_debug.h: -------------------------------------------------------------------------------- 1 | // Caide C++ inliner 2 | // 3 | // This file is distributed under the GNU General Public License as published by 4 | // the Free Software Foundation, either version 3 of the License, or (at your 5 | // option) any later version. See LICENSE.TXT for details. 6 | 7 | #pragma once 8 | 9 | #ifdef CAIDE_DEBUG_MODE 10 | 11 | #include 12 | 13 | #define dbg(vals) std::cerr << vals 14 | #define CAIDE_FUNC __FUNCTION__ << std::endl 15 | 16 | #else 17 | 18 | #define dbg(vals) 19 | #define CAIDE_FUNC "" 20 | 21 | #endif 22 | 23 | -------------------------------------------------------------------------------- /src/clang_compat.cpp: -------------------------------------------------------------------------------- 1 | // Caide C++ inliner 2 | // 3 | // This file is distributed under the GNU General Public License as published by 4 | // the Free Software Foundation, either version 3 of the License, or (at your 5 | // option) any later version. See LICENSE.TXT for details. 6 | 7 | #include "clang_compat.h" 8 | #include "clang_version.h" 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | 16 | 17 | using namespace clang; 18 | 19 | namespace caide { namespace internal { 20 | 21 | CXXConstructorDecl* getInheritedConstructor(CXXConstructorDecl* ctorDecl) { 22 | #if CAIDE_CLANG_VERSION_AT_LEAST(3,9) 23 | return ctorDecl->getInheritedConstructor().getConstructor(); 24 | #else 25 | return const_cast(ctorDecl->getInheritedConstructor()); 26 | #endif 27 | } 28 | 29 | 30 | SourceLocation getBeginLoc(const Decl* decl) { 31 | #if CAIDE_CLANG_VERSION_AT_LEAST(8,0) 32 | return decl->getBeginLoc(); 33 | #else 34 | return decl->getLocStart(); 35 | #endif 36 | } 37 | 38 | SourceLocation getEndLoc(const Decl* decl) { 39 | #if CAIDE_CLANG_VERSION_AT_LEAST(8,0) 40 | return decl->getEndLoc(); 41 | #else 42 | return decl->getLocEnd(); 43 | #endif 44 | } 45 | 46 | SourceLocation getBeginLoc(const RawComment* comment) { 47 | #if CAIDE_CLANG_VERSION_AT_LEAST(8,0) 48 | return comment->getBeginLoc(); 49 | #else 50 | return comment->getLocStart(); 51 | #endif 52 | } 53 | 54 | SourceLocation getEndLoc(const RawComment* comment) { 55 | #if CAIDE_CLANG_VERSION_AT_LEAST(8,0) 56 | return comment->getEndLoc(); 57 | #else 58 | return comment->getLocEnd(); 59 | #endif 60 | } 61 | 62 | bool isWrittenInBuiltinFile(const SourceManager& srcManager, SourceLocation location) { 63 | #if CAIDE_CLANG_VERSION_AT_LEAST(8,0) 64 | return srcManager.isWrittenInBuiltinFile(location); 65 | #else 66 | // Copied from latest SourceManager::isWrittenInBuiltinFile. 67 | const char* fileName = srcManager.getPresumedLoc(location).getFilename(); 68 | return std::strcmp(fileName, "") == 0; 69 | #endif 70 | } 71 | 72 | unsigned getNumArgs(const clang::TemplateSpecializationType& templateSpecType) { 73 | #if CAIDE_CLANG_VERSION_AT_LEAST(13,0) 74 | return templateSpecType.template_arguments().size(); 75 | #else 76 | return templateSpecType.getNumArgs(); 77 | #endif 78 | } 79 | 80 | const clang::TemplateArgument* getArgs(const TemplateSpecializationType& templateSpecType) { 81 | #if CAIDE_CLANG_VERSION_AT_LEAST(13,0) 82 | return templateSpecType.template_arguments().data(); 83 | #else 84 | return templateSpecType.getArgs(); 85 | #endif 86 | } 87 | }} 88 | 89 | -------------------------------------------------------------------------------- /src/clang_compat.h: -------------------------------------------------------------------------------- 1 | // Caide C++ inliner 2 | // 3 | // This file is distributed under the GNU General Public License as published by 4 | // the Free Software Foundation, either version 3 of the License, or (at your 5 | // option) any later version. See LICENSE.TXT for details. 6 | 7 | #pragma once 8 | 9 | #include 10 | 11 | namespace clang { 12 | class CXXConstructorDecl; 13 | class Decl; 14 | class RawComment; 15 | class SourceManager; 16 | class TemplateArgument; 17 | class TemplateSpecializationType; 18 | } 19 | 20 | namespace caide { namespace internal { 21 | 22 | clang::CXXConstructorDecl* getInheritedConstructor(clang::CXXConstructorDecl* ctorDecl); 23 | clang::SourceLocation getBeginLoc(const clang::Decl* decl); 24 | clang::SourceLocation getEndLoc(const clang::Decl* decl); 25 | clang::SourceLocation getBeginLoc(const clang::RawComment* comment); 26 | clang::SourceLocation getEndLoc(const clang::RawComment* comment); 27 | bool isWrittenInBuiltinFile(const clang::SourceManager& srcManager, 28 | clang::SourceLocation location); 29 | 30 | unsigned getNumArgs(const clang::TemplateSpecializationType& templateSpecType); 31 | const clang::TemplateArgument* getArgs( 32 | const clang::TemplateSpecializationType& templateSpecType); 33 | 34 | }} 35 | 36 | -------------------------------------------------------------------------------- /src/clang_version.h: -------------------------------------------------------------------------------- 1 | // Caide C++ inliner 2 | // 3 | // This file is distributed under the GNU General Public License as published by 4 | // the Free Software Foundation, either version 3 of the License, or (at your 5 | // option) any later version. See LICENSE.TXT for details. 6 | 7 | #pragma once 8 | #include 9 | 10 | #define CAIDE_CLANG_VERSION_AT_LEAST(major, minor) \ 11 | ((major) < CLANG_VERSION_MAJOR || \ 12 | (major) == CLANG_VERSION_MAJOR && (minor) <= CLANG_VERSION_MINOR) 13 | 14 | -------------------------------------------------------------------------------- /src/cmd/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(cmd cmd.cpp) 2 | target_link_libraries(cmd caideInliner) 3 | 4 | -------------------------------------------------------------------------------- /src/cmd/cmd.cpp: -------------------------------------------------------------------------------- 1 | // Caide C++ inliner 2 | // 3 | // This file is distributed under the GNU General Public License as published by 4 | // the Free Software Foundation, either version 3 of the License, or (at your 5 | // option) any later version. See LICENSE.TXT for details. 6 | 7 | #include "../caideInliner.hpp" 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | 16 | using namespace std; 17 | 18 | int main(int argc, const char* argv[]) { 19 | vector sourceFiles; 20 | string tmpDirectory = "./caide-tmp"; 21 | string outputFile = "./caide-tmp/result.cpp"; 22 | vector clangOptions; 23 | vector macrosToKeep; 24 | int maxConsecutiveEmptyLines = 2; 25 | 26 | const string clangOptionsEnd = "--"; 27 | const string directoryFlag = "-d"; 28 | const string outputFlag = "-o"; 29 | const string keepMacrosFlag = "-k"; 30 | const string emptyLinesFlag = "-l"; 31 | 32 | int i = 1; 33 | for (; i < argc && clangOptionsEnd != argv[i]; ++i) { 34 | if (argv[i][0] != '@') { 35 | clangOptions.emplace_back(argv[i]); 36 | } else { 37 | ifstream in(&argv[i][1]); 38 | string line; 39 | while (getline(in, line)) { 40 | clangOptions.emplace_back(line); 41 | } 42 | } 43 | } 44 | 45 | for (++i; i < argc; ++i) { 46 | if (directoryFlag == argv[i]) { 47 | ++i; 48 | if (i < argc) tmpDirectory = argv[i]; 49 | } else if (outputFlag == argv[i]) { 50 | ++i; 51 | if (i < argc) outputFile = argv[i]; 52 | } else if (keepMacrosFlag == argv[i]) { 53 | ++i; 54 | if (i < argc) macrosToKeep.emplace_back(argv[i]); 55 | } else if (emptyLinesFlag == argv[i]) { 56 | ++i; 57 | if (i < argc) maxConsecutiveEmptyLines = strtol(argv[i], nullptr, 10); 58 | } else { 59 | sourceFiles.emplace_back(argv[i]); 60 | } 61 | } 62 | 63 | caide::CppInliner inliner(tmpDirectory); 64 | inliner.clangCompilationOptions.swap(clangOptions); 65 | inliner.macrosToKeep.insert(inliner.macrosToKeep.end(), 66 | macrosToKeep.begin(), macrosToKeep.end()); 67 | inliner.maxConsequentEmptyLines = maxConsecutiveEmptyLines; 68 | inliner.inlineCode(sourceFiles, outputFile); 69 | 70 | return 0; 71 | } 72 | 73 | -------------------------------------------------------------------------------- /src/detect_options.cpp: -------------------------------------------------------------------------------- 1 | // Caide C++ inliner 2 | // 3 | // This file is distributed under the GNU General Public License as published by 4 | // the Free Software Foundation, either version 3 of the License, or (at your 5 | // option) any later version. See LICENSE.TXT for details. 6 | 7 | #include "clang_version.h" 8 | #include "util.h" 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | using std::string; 26 | using std::vector; 27 | 28 | using namespace clang; 29 | 30 | namespace caide { 31 | namespace internal { 32 | 33 | namespace { 34 | 35 | string trim(const string& s) { 36 | std::size_t i = s.find_first_not_of(" "); 37 | std::size_t j = s.find_last_not_of(" "); 38 | if (i != string::npos && j != string::npos && i <= j) 39 | return s.substr(i, j - i + 1); 40 | return s; 41 | } 42 | 43 | bool startsWith(const string& s, const string& prefix) { 44 | return s.length() >= prefix.length() && 45 | std::mismatch(prefix.begin(), prefix.end(), s.begin()).first == prefix.end(); 46 | } 47 | 48 | bool endsWith(const string& s, const string& prefix) { 49 | return s.length() >= prefix.length() && 50 | std::mismatch(prefix.rbegin(), prefix.rend(), s.rbegin()).first == prefix.rend(); 51 | } 52 | 53 | string pathConcat(const string& directory, const string& fileName) { 54 | return directory + "/" + fileName; 55 | } 56 | 57 | class DetectOptionsASTConsumer: public ASTConsumer { 58 | public: 59 | virtual void HandleTranslationUnit(ASTContext& Ctx) override { 60 | Ctx.getDiagnostics().setSuppressAllDiagnostics(true); 61 | ASTConsumer::HandleTranslationUnit(Ctx); 62 | } 63 | }; 64 | 65 | class DetectOptionsFrontendAction: public ASTFrontendAction { 66 | virtual std::unique_ptr CreateASTConsumer(CompilerInstance&, StringRef /*file*/) override { 67 | return std::unique_ptr(new DetectOptionsASTConsumer()); 68 | } 69 | }; 70 | 71 | class DetectOptionsFrontendActionFactory: public clang::tooling::FrontendActionFactory { 72 | public: 73 | #if CAIDE_CLANG_VERSION_AT_LEAST(10, 0) 74 | std::unique_ptr create() override { 75 | return std::make_unique(); 76 | } 77 | #else 78 | FrontendAction* create() override { 79 | return new DetectOptionsFrontendAction(); 80 | } 81 | #endif 82 | }; 83 | 84 | bool testOptions(const vector& compilationOptions, const string& cppFile) { 85 | std::unique_ptr compilationDatabase( 86 | createCompilationDatabaseFromCommandLine(compilationOptions)); 87 | 88 | vector sources{cppFile}; 89 | DetectOptionsFrontendActionFactory factory; 90 | clang::tooling::ClangTool tool(*compilationDatabase, sources); 91 | return tool.run(&factory) == 0; 92 | } 93 | 94 | } // anonymous namespace 95 | 96 | vector detectClangOptions(const string& temporaryDirectory) { 97 | vector gccLikeCompilers; 98 | if (const char* cxx = ::getenv("CXX")) 99 | gccLikeCompilers.push_back(cxx); 100 | vector options{"g++", "clang++"}; 101 | for (const auto& s : options) { 102 | if (gccLikeCompilers.end() == std::find(gccLikeCompilers.begin(), gccLikeCompilers.end(), s)) { 103 | gccLikeCompilers.push_back(s); 104 | } 105 | } 106 | 107 | const string emptySourceFile = pathConcat(temporaryDirectory, "empty.cpp"); 108 | const string outputExeFile = pathConcat(temporaryDirectory, "detect.exe"); 109 | const string gccLogFile = pathConcat(temporaryDirectory, "gcclog.txt"); 110 | const string detectSourceFile = pathConcat(temporaryDirectory, "detect.cpp"); 111 | (void)std::ofstream(emptySourceFile.c_str()); 112 | std::ofstream detectFile(detectSourceFile.c_str()); 113 | detectFile << 114 | "#include \n#include \n#include \n#include \n" 115 | "#include \n#include \n#include \n#include \n" 116 | "#include \n#include \n#include \n#include \n" 117 | "#include \n#include \n#include \n#include \n" 118 | "#include \n#include \n#include \n#include \n" 119 | "#include \n#include \n#include \n#include \n" 120 | "#include \n#include \n#include \n#include \n#include \n" 121 | "#include \n#include \n#include \n#include \n" 122 | "#include \n#include \n#include \n#include \n" 123 | "#include \n#include \n#include \n#include \n#include \n" 124 | "#include \n#include \n#include \n#include \n" 125 | "#include \n#include \n#include \n" 126 | "int main() { return 0; }\n"; 127 | detectFile.close(); 128 | 129 | for (const auto& cxx: gccLikeCompilers) { 130 | // TODO: escape whitespace and quotes. 131 | // TODO: use correct locale somehow. 132 | std::stringstream command; 133 | command << cxx << " -x c++ -c -v " << emptySourceFile << " -o " << outputExeFile << " 2>" << gccLogFile; 134 | std::system(command.str().c_str()); 135 | std::ifstream logFile(gccLogFile.c_str()); 136 | string line; 137 | bool isSearchList = false; 138 | vector compilationOptions; 139 | while (std::getline(logFile, line)) { 140 | if (startsWith(line, "End of search list")) 141 | break; 142 | 143 | if (endsWith(line, "search starts here:")) 144 | isSearchList = true; 145 | else if (isSearchList) { 146 | compilationOptions.push_back("-isystem"); 147 | compilationOptions.push_back(trim(line)); 148 | } 149 | } 150 | 151 | if (compilationOptions.empty()) 152 | continue; 153 | 154 | compilationOptions.push_back("-nostdlibinc"); 155 | compilationOptions.push_back("-nobuiltininc"); 156 | if (testOptions(compilationOptions, detectSourceFile)) 157 | return compilationOptions; 158 | 159 | compilationOptions.pop_back(); 160 | if (testOptions(compilationOptions, detectSourceFile)) 161 | return compilationOptions; 162 | } 163 | 164 | return {}; // let clang determine the options automatically 165 | } 166 | 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src/detect_options.h: -------------------------------------------------------------------------------- 1 | // Caide C++ inliner 2 | // 3 | // This file is distributed under the GNU General Public License as published by 4 | // the Free Software Foundation, either version 3 of the License, or (at your 5 | // option) any later version. See LICENSE.TXT for details. 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | 12 | namespace caide { 13 | namespace internal { 14 | 15 | std::vector detectClangOptions(const std::string& temporaryDirectory); 16 | 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/inliner.h: -------------------------------------------------------------------------------- 1 | // Caide C++ inliner 2 | // 3 | // This file is distributed under the GNU General Public License as published by 4 | // the Free Software Foundation, either version 3 of the License, or (at your 5 | // option) any later version. See LICENSE.TXT for details. 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | namespace caide { 14 | namespace internal { 15 | 16 | // First inliner stage: inline included headers 17 | class Inliner { 18 | public: 19 | explicit Inliner(const std::vector& clangCommandLineOptions); 20 | 21 | // The file is read in binary mode, so the returned string is also 22 | // 'in binary mode' (contains \r\n on Windows) 23 | std::string doInline(const std::string& cppFile); 24 | 25 | // Return compilation options for the inlined file. Normally, they match 26 | // compilation options for the inliner provided in the constructor. But if 27 | // the original options contained an '-include FileName' option and FileName 28 | // has been inlined, this option will be removed to avoid redefinition. 29 | std::vector getResultingCommandLineOptions() const; 30 | 31 | private: 32 | std::vector cmdLineOptions; 33 | std::unordered_set includedHeaders; 34 | std::vector inlineResults; 35 | std::unordered_set inlinedPathsFromCommandLine; 36 | }; 37 | 38 | } 39 | } 40 | 41 | -------------------------------------------------------------------------------- /src/optimizer.h: -------------------------------------------------------------------------------- 1 | // Caide C++ inliner 2 | // 3 | // This file is distributed under the GNU General Public License as published by 4 | // the Free Software Foundation, either version 3 of the License, or (at your 5 | // option) any later version. See LICENSE.TXT for details. 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | namespace caide { 15 | namespace internal { 16 | 17 | // Second inliner stage: remove unused code 18 | class Optimizer { 19 | public: 20 | Optimizer(const std::vector& cmdLineOptions, 21 | const std::vector& macrosToKeep, 22 | const std::vector& identifiersToKeep); 23 | 24 | // The file is read in binary mode, so the returned string is also 25 | // 'in binary mode' (contains \r\n on Windows) 26 | std::string doOptimize(const std::string& cppFile); 27 | 28 | private: 29 | std::vector cmdLineOptions; 30 | std::set macrosToKeep; 31 | std::unordered_set identifiersToKeep; 32 | }; 33 | 34 | } 35 | } 36 | 37 | -------------------------------------------------------------------------------- /src/sema_utils.cpp: -------------------------------------------------------------------------------- 1 | // Caide C++ inliner 2 | // 3 | // This file is distributed under the GNU General Public License as published by 4 | // the Free Software Foundation, either version 3 of the License, or (at your 5 | // option) any later version. See LICENSE.TXT for details. 6 | 7 | #include "sema_utils.h" 8 | 9 | // #define CAIDE_DEBUG_MODE 10 | #include "caide_debug.h" 11 | #include "clang_version.h" 12 | #include "util.h" 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | 20 | using namespace clang; 21 | 22 | namespace caide { namespace internal { 23 | 24 | SuppressErrorsInScope::SuppressErrorsInScope(Sema& sema_): 25 | sema(sema_) 26 | { 27 | DiagnosticsEngine& diag = sema.getDiagnostics(); 28 | origSuppressAllDiagnostics = diag.getSuppressAllDiagnostics(); 29 | diag.setSuppressAllDiagnostics(true); 30 | } 31 | 32 | SuppressErrorsInScope::~SuppressErrorsInScope() { 33 | sema.getDiagnostics().setSuppressAllDiagnostics(origSuppressAllDiagnostics); 34 | } 35 | 36 | namespace { 37 | 38 | // Calculates number of template levels inside which decl is wrapped. 39 | // If decl itself is a template, it is not counted. 40 | unsigned getNumTemplateLevels(const Decl* decl) { 41 | unsigned res = 0; 42 | for (const DeclContext* ctx = decl->getDeclContext(); ctx; ctx = ctx->getParent()) { 43 | if (isa(ctx)) { 44 | ++res; 45 | } else if (const auto* record = dyn_cast(ctx)) { 46 | if (record->getDescribedClassTemplate()) { 47 | ++res; 48 | } 49 | } else if (const auto* func = dyn_cast(ctx)) { 50 | if (func->getDescribedFunctionTemplate()) { 51 | ++res; 52 | } 53 | } 54 | } 55 | 56 | return res; 57 | } 58 | 59 | bool anyArgDependent(llvm::ArrayRef args) { 60 | for (const auto& arg : args) { 61 | if (arg.isDependent()) { 62 | return true; 63 | } 64 | } 65 | return false; 66 | } 67 | 68 | void substituteExprs(Sema& sema, llvm::ArrayRef exprs, 69 | const MultiLevelTemplateArgumentList& MLTAL, SugaredSignature& sig) 70 | { 71 | for (auto* expr : exprs) { 72 | clang::ExprResult result = sema.SubstExpr(const_cast(expr), MLTAL); 73 | if (result.isInvalid()) { 74 | dbg("sema.SubstExpr failed" << std::endl); 75 | } else if (Expr* res = result.get()) { 76 | dbg("Specialized constraint expr: " << toString(sema.getASTContext(), *res) << std::endl); 77 | sig.associatedConstraints.push_back(res); 78 | } 79 | } 80 | } 81 | 82 | // sema.DeduceTemplateArguments was exposed in commit 7415524b 83 | #if CAIDE_CLANG_VERSION_AT_LEAST(19, 1) 84 | 85 | llvm::SmallVector getInjectedTemplateArgs( 86 | ASTContext& astCtx, const TemplateParameterList& params) 87 | { 88 | llvm::SmallVector Ps; 89 | for (NamedDecl* paramDecl : params) 90 | Ps.push_back(astCtx.getInjectedTemplateArg(paramDecl)); 91 | return Ps; 92 | } 93 | 94 | template 95 | int deduceTemplateArguments(Sema& sema, 96 | TemplateDecl* templateDecl, 97 | llvm::ArrayRef writtenArgs, 98 | llvm::SmallVectorImpl& deduced, 99 | TArgumentLocVector& synthesizedArgLocs) 100 | { 101 | const bool isFuncTemplate = isa(templateDecl); 102 | TemplateParameterList* params = templateDecl->getTemplateParameters(); 103 | 104 | // If deduced is not empty, we know unsugared form of template arguments 105 | // from the specialization decl. But we still want to synthesize sugared form 106 | // of default arguments. 107 | // If deduced is empty, specialization decl is not available (e.g for template type aliases). 108 | if (deduced.empty()) { 109 | llvm::SmallVector Ps = getInjectedTemplateArgs(sema.getASTContext(), *params); 110 | llvm::SmallVector deducedTemplateArgs(params->size()); 111 | clang::sema::TemplateDeductionInfo deductionInfo( 112 | templateDecl->getBeginLoc(), 113 | getNumTemplateLevels(templateDecl)); 114 | 115 | // Collects the last few arguments into a pack to match the parameter pack 116 | // (if any), but otherwise doesn't change arguments. 117 | int deductionResult = (int)sema.DeduceTemplateArguments(params, Ps, writtenArgs, 118 | deductionInfo, deducedTemplateArgs, false); 119 | if (deductionResult != 0) { 120 | return deductionResult; 121 | } 122 | 123 | deduced.assign(deducedTemplateArgs.begin(), deducedTemplateArgs.end()); 124 | } 125 | 126 | // TODO: use correct source locations. Probably only matters in case of 127 | // compilation errors, which we don't expect here. 128 | SourceLocation templateLoc = templateDecl->getLocation(); 129 | SourceLocation rAngleLoc = templateLoc; 130 | 131 | // Instantiate necessary default arguments. For function templates, some 132 | // template arguments could have been deduced from function arguments 133 | // and we don't know which template arguments were defaulted, apart from 134 | // explicitly provided arguments. Unnecessary instantiations can lead to 135 | // false positives or compilation errors (which we suppress in outer scope). 136 | for (unsigned i = writtenArgs.size(); i < params->size(); ++i) { 137 | bool hasDefaultArg = false; 138 | NamedDecl* param = params->getParam(i); 139 | // We pass 'deduced' as both SugaredConverted and CanonicalConverted arguments; 140 | // the latter doesn't seem to be used anyway... 141 | TemplateArgumentLoc defaultArgLoc = sema.SubstDefaultTemplateArgumentIfAvailable( 142 | templateDecl, templateLoc, rAngleLoc, param, deduced, deduced, hasDefaultArg); 143 | if (hasDefaultArg) { 144 | synthesizedArgLocs.push_back(defaultArgLoc); 145 | if (deduced[i].isNull()) { 146 | // Not filled by sema.DeduceTemplateArguments above. 147 | deduced[i] = defaultArgLoc.getArgument(); 148 | } 149 | } else if (!isFuncTemplate) { 150 | // Something unexpected: no argument provided and no default argument. 151 | return -1; 152 | } 153 | } 154 | 155 | return 0; 156 | } 157 | 158 | void collectSugar(Sema& sema, 159 | TemplateDecl* templateDecl, 160 | const MultiLevelTemplateArgumentList& MLTAL, 161 | SugaredSignature& sig) 162 | { 163 | TemplateParameterList* paramList = templateDecl->getTemplateParameters(); 164 | 165 | // Substitute sugared template arguments into template parameter types. 166 | for (NamedDecl* paramDecl : paramList->asArray()) { 167 | if (const auto* NTTP = dyn_cast(paramDecl)) { 168 | TypeSourceInfo* substType = sema.SubstType( 169 | NTTP->getTypeSourceInfo(), 170 | MLTAL, 171 | NTTP->getBeginLoc(), 172 | {}); 173 | if (substType) { 174 | // substType->getType().dump(); 175 | sig.argTypes.push_back(substType); 176 | } else { 177 | dbg("sema.SubstType failed" << std::endl); 178 | } 179 | } 180 | } 181 | 182 | llvm::SmallVector constraints; 183 | templateDecl->getAssociatedConstraints(constraints); 184 | substituteExprs(sema, constraints, MLTAL, sig); 185 | } 186 | #endif 187 | 188 | } 189 | 190 | SugaredSignature substituteTemplateArguments( 191 | Sema& sema, TemplateDecl* templateDecl, 192 | llvm::ArrayRef writtenArgs, 193 | llvm::ArrayRef args) 194 | { 195 | SugaredSignature ret; 196 | 197 | #if CAIDE_CLANG_VERSION_AT_LEAST(19, 1) 198 | if (anyArgDependent(writtenArgs)) { 199 | return ret; 200 | } 201 | 202 | SuppressErrorsInScope guard(sema); 203 | 204 | llvm::SmallVector deducedArgs(args); 205 | 206 | int deductionResult = deduceTemplateArguments(sema, templateDecl, writtenArgs, deducedArgs, ret.templateArgLocs); 207 | if (deductionResult != 0) { 208 | dbg("sema.DeduceTemplateArguments failed: " << deductionResult << std::endl); 209 | return ret; 210 | } 211 | 212 | if (anyArgDependent(deducedArgs)) { 213 | return ret; 214 | } 215 | 216 | MultiLevelTemplateArgumentList MLTAL; 217 | MLTAL.addOuterTemplateArguments(templateDecl, deducedArgs, true); 218 | // TODO: are we handling nested templates properly? 219 | MLTAL.addOuterRetainedLevels(getNumTemplateLevels(templateDecl)); 220 | 221 | // Push the instantiation on context stack till the end of scope. 222 | // This is only to avoid clang assertions in debug mode. 223 | Sema::InstantiatingTemplate substitutionContext( 224 | sema, templateDecl->getBeginLoc(), templateDecl); 225 | if (substitutionContext.isInvalid()) { 226 | dbg("Failed to create instantiation context" << std::endl); 227 | return ret; 228 | } 229 | 230 | collectSugar(sema, templateDecl, MLTAL, ret); 231 | if (auto* conceptDecl = dyn_cast(templateDecl)) { 232 | Expr* constraintExpr = conceptDecl->getConstraintExpr(); 233 | substituteExprs(sema, {constraintExpr}, MLTAL, ret); 234 | } 235 | if (auto* ftemplate = dyn_cast(templateDecl)) { 236 | // Substitute sugared template arguments into function parameter types. 237 | FunctionDecl* func = ftemplate->getTemplatedDecl(); // Non-specialized decl. 238 | for (ParmVarDecl* param : func->parameters()) { 239 | TypeSourceInfo* paramTSI = param->getTypeSourceInfo(); 240 | TypeSourceInfo* substType = sema.SubstType( 241 | paramTSI, 242 | MLTAL, 243 | param->getLocation(), 244 | {}); 245 | if (substType) { 246 | // substType->getType().dump(); 247 | ret.argTypes.push_back(substType); 248 | } else { 249 | dbg("sema.SubstType failed" << std::endl); 250 | } 251 | } 252 | } 253 | 254 | #else 255 | (void)sema; 256 | (void)templateDecl; 257 | (void)writtenArgs; 258 | (void)args; 259 | #endif 260 | 261 | return ret; 262 | } 263 | 264 | SugaredSignature substituteTemplateArguments( 265 | Sema& sema, ClassTemplatePartialSpecializationDecl* templateDecl, 266 | llvm::ArrayRef args) { 267 | SugaredSignature ret; 268 | 269 | #if CAIDE_CLANG_VERSION_AT_LEAST(16, 0) 270 | if (anyArgDependent(args)) { 271 | return ret; 272 | } 273 | 274 | MultiLevelTemplateArgumentList sugaredMLTAL; 275 | sugaredMLTAL.addOuterTemplateArguments(templateDecl, args, true); 276 | // TODO: are we handling nested templates properly? 277 | sugaredMLTAL.addOuterRetainedLevels(getNumTemplateLevels(templateDecl)); 278 | 279 | TemplateArgumentListInfo substitutedTemplateArgs; 280 | const ASTTemplateArgumentListInfo* templateArgsAsWritten = templateDecl->getTemplateArgsAsWritten(); 281 | if (!templateArgsAsWritten) { 282 | return ret; 283 | } 284 | 285 | clang::sema::TemplateDeductionInfo deductionInfo( 286 | templateDecl->getBeginLoc(), 287 | getNumTemplateLevels(templateDecl)); 288 | llvm::SmallVector argsToDeduce; 289 | argsToDeduce.resize(templateDecl->getTemplateParameters()->size()); 290 | // Push the instantiation on context stack till the end of scope. 291 | // This is only to avoid clang assertions in debug mode. 292 | // 293 | // We could use sema.DeduceTemplateArguments which pushes and pops this internally. 294 | // But it would probably be more expensive to perform the entire deduction, plus we want to 295 | // substitute actually written (sugared) arguments of the specialization. 296 | Sema::InstantiatingTemplate substitutionContext( 297 | sema, deductionInfo.getLocation(), templateDecl, argsToDeduce, 298 | deductionInfo); 299 | if (substitutionContext.isInvalid()) { 300 | dbg("Failed to create instantiation context for SubstTemplateArguments"); 301 | } else { 302 | SuppressErrorsInScope guard(sema); 303 | bool error = sema.SubstTemplateArguments(templateArgsAsWritten->arguments(), 304 | sugaredMLTAL, substitutedTemplateArgs); 305 | if (error) { 306 | dbg("sema.SubstTemplateArguments failed" << std::endl); 307 | } else { 308 | for (unsigned i = 0; i < substitutedTemplateArgs.size(); ++i) 309 | ret.templateArgLocs.push_back(substitutedTemplateArgs[i]); 310 | } 311 | } 312 | 313 | llvm::SmallVector constraints; 314 | templateDecl->getAssociatedConstraints(constraints); 315 | substituteExprs(sema, constraints, sugaredMLTAL, ret); 316 | 317 | #else 318 | (void)sema; 319 | (void)templateDecl; 320 | (void)args; 321 | #endif 322 | 323 | return ret; 324 | } 325 | 326 | }} 327 | 328 | -------------------------------------------------------------------------------- /src/sema_utils.h: -------------------------------------------------------------------------------- 1 | // Caide C++ inliner 2 | // 3 | // This file is distributed under the GNU General Public License as published by 4 | // the Free Software Foundation, either version 3 of the License, or (at your 5 | // option) any later version. See LICENSE.TXT for details. 6 | 7 | #pragma once 8 | 9 | #include 10 | 11 | #include 12 | 13 | namespace clang { 14 | class Expr; 15 | class Sema; 16 | class ClassTemplatePartialSpecializationDecl; 17 | class TemplateArgument; 18 | class TemplateArgumentLoc; 19 | class TemplateDecl; 20 | class TypeSourceInfo; 21 | } 22 | 23 | namespace caide { namespace internal { 24 | 25 | class SuppressErrorsInScope { 26 | public: 27 | explicit SuppressErrorsInScope(clang::Sema& sema_); 28 | ~SuppressErrorsInScope(); 29 | 30 | private: 31 | clang::Sema& sema; 32 | bool origSuppressAllDiagnostics; 33 | }; 34 | 35 | // Sugared form of some entities emerging during a specific template instantiation. 36 | struct SugaredSignature { 37 | // Defaulted template arguments. 38 | std::vector templateArgLocs; 39 | 40 | // Sugared constraints, including concept constraints, requires clauses and 41 | // (for concept instantiation) constraint expression in the concept definition. 42 | std::vector associatedConstraints; 43 | 44 | // Sugared types of non-type template parameters and (for function template 45 | // instantiation) sugared function argument types. 46 | std::vector argTypes; 47 | }; 48 | 49 | SugaredSignature substituteTemplateArguments( 50 | clang::Sema&, clang::TemplateDecl*, 51 | llvm::ArrayRef writtenArgs, 52 | llvm::ArrayRef args); 53 | 54 | SugaredSignature substituteTemplateArguments( 55 | clang::Sema&, clang::ClassTemplatePartialSpecializationDecl*, 56 | llvm::ArrayRef args); 57 | 58 | }} 59 | 60 | -------------------------------------------------------------------------------- /src/test-tool/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(test-tool test-tool.cpp) 2 | 3 | target_link_libraries(test-tool caideInliner) 4 | 5 | set(tests_dir "${CMAKE_SOURCE_DIR}/../tests/cases") 6 | set(tests_temp_dir "${CMAKE_SOURCE_DIR}/../tests/temp") 7 | set(clang_options_file "${CMAKE_CURRENT_BINARY_DIR}/clangOptions.txt") 8 | 9 | add_custom_command(TARGET test-tool POST_BUILD 10 | COMMAND test-tool "${tests_temp_dir}" "--prepare" "${clang_options_file}" 11 | COMMENT "Detecting compilation options") 12 | 13 | # To run tests: make test-tool && ctest 14 | # To run a specific test: ctest -R 15 | # For verbose output: ctest --verbose 16 | 17 | set(test_list actually-written-type alias-in-template-argument base-class-of-template base-initializers caide-concept-comment delayed-parsing friends github-issue17 github-issue4 ident-to-keep include-option-std include-option-user inheriting-ctor inliner1 inliner2 inliner3 line-directives macros merge-namespaces merge-namespaces-2 pull-headers-up qualifiers references-from-template-arguments remove-comments remove-namespaces remove-template-functions remove-type-alias sizeof source-ranges static-assert std-namespace stl template-alias templated-context template-friend template-variables track-parent-decls ull unused-fields using-declarations) 18 | 19 | function(add_test_directory test_name) 20 | add_test(NAME ${test_name} 21 | COMMAND test-tool "${tests_temp_dir}" "${clang_options_file}" "${tests_dir}/${test_name}") 22 | set_tests_properties(${test_name} PROPERTIES REQUIRED_FILES "${clang_options_file}") 23 | endfunction() 24 | 25 | foreach(test_name IN LISTS test_list) 26 | add_test_directory(${test_name}) 27 | endforeach() 28 | 29 | if(LLVM_PACKAGE_VERSION VERSION_GREATER_EQUAL "14") 30 | # Needs https://github.com/llvm/llvm-project/commit/4e4511df8d33a6fc02d5e46c681855db495187cd 31 | add_test_directory(enums) 32 | endif() 33 | 34 | if(LLVM_PACKAGE_VERSION VERSION_GREATER_EQUAL "19") 35 | # Need Sema APIs that are unavailable or too different in earlier versions. 36 | add_test_directory(github-issue8) 37 | add_test_directory(concepts-constraints) 38 | endif() 39 | -------------------------------------------------------------------------------- /src/test-tool/test-tool.cpp: -------------------------------------------------------------------------------- 1 | // Caide C++ inliner 2 | // 3 | // This file is distributed under the GNU General Public License as published by 4 | // the Free Software Foundation, either version 3 of the License, or (at your 5 | // option) any later version. See LICENSE.TXT for details. 6 | 7 | #include "../caideInliner.hpp" 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | 18 | using std::ifstream; 19 | using std::string; 20 | using std::vector; 21 | 22 | static vector readNonEmptyLines(const string& filePath) { 23 | vector lines; 24 | ifstream file{filePath.c_str()}; 25 | string line; 26 | 27 | while (std::getline(file, line)) { 28 | if (line.find_first_not_of(" \t\r\n") != string::npos) 29 | lines.push_back(line); 30 | } 31 | 32 | return lines; 33 | } 34 | 35 | static string pathConcat(const string& directory, const string& fileName) { 36 | return directory + "/" + fileName; 37 | } 38 | 39 | static bool runTest(const string& testDirectory, const string& tempDirectory, caide::CppInliner inliner) { 40 | // Setup 41 | vector cppFiles = readNonEmptyLines(pathConcat(testDirectory, "fileList.txt")); 42 | for (string& s : cppFiles) 43 | s = pathConcat(testDirectory, s); 44 | 45 | for (int i = 1; i <= 9; ++i) { 46 | std::ostringstream fileName; 47 | fileName << i << ".cpp"; 48 | const string filePath = pathConcat(testDirectory, fileName.str()); 49 | ifstream file{filePath.c_str()}; 50 | if (file) 51 | cppFiles.push_back(filePath); 52 | } 53 | 54 | if (cppFiles.empty()) { 55 | throw std::runtime_error("No source files found in test directory"); 56 | } 57 | 58 | vector additionalOptions = readNonEmptyLines(pathConcat(testDirectory, "clangOptions.txt")); 59 | for (string& opt : additionalOptions) { 60 | const static string TEST_ROOT_MARKER = "TEST_ROOT"; 61 | auto p = opt.find(TEST_ROOT_MARKER); 62 | if (p != string::npos) { 63 | opt.replace(p, TEST_ROOT_MARKER.length(), testDirectory); 64 | } 65 | 66 | inliner.clangCompilationOptions.push_back(std::move(opt)); 67 | } 68 | 69 | const char* verbose = std::getenv("CAIDE_TEST_VERBOSE"); 70 | if (verbose && *verbose == '1') 71 | inliner.clangCompilationOptions.push_back("-v"); 72 | 73 | inliner.macrosToKeep = readNonEmptyLines(pathConcat(testDirectory, "macrosToKeep.txt")); 74 | inliner.identifiersToKeep = readNonEmptyLines(pathConcat(testDirectory, "identifiersToKeep.txt")); 75 | 76 | const string outputFilePath = pathConcat(tempDirectory, "result.cpp"); 77 | 78 | // Run 79 | inliner.inlineCode(cppFiles, outputFilePath); 80 | 81 | // Assert 82 | const string etalonFilePath = pathConcat(testDirectory, "etalon.cpp"); 83 | const vector output = readNonEmptyLines(outputFilePath); 84 | const vector etalon = readNonEmptyLines(etalonFilePath); 85 | 86 | const int minLength = (int)std::min(output.size(), etalon.size()); 87 | for (int i = 0; i < minLength; ++i) { 88 | // TODO: Print line numbers 89 | if (output[i] != etalon[i]) { 90 | std::cout 91 | << "< " << etalon[i] << "\n" 92 | << "> " << output[i] << "\n"; 93 | return false; 94 | } 95 | } 96 | 97 | if (output.size() < etalon.size()) { 98 | std::cout << "Unexpected end of file: " << outputFilePath << "\n"; 99 | return false; 100 | } 101 | 102 | if (output.size() > etalon.size()) { 103 | std::cout << "Unexpected end of file: " << etalonFilePath << "\n"; 104 | return false; 105 | } 106 | 107 | return true; 108 | } 109 | 110 | 111 | int main(int argc, char* argv[]) { 112 | if (argc < 3) { 113 | std::cout << "Usage: test-tool [...]\n"; 114 | return 1; 115 | } 116 | 117 | const string tempDirectory{argv[1]}; 118 | 119 | caide::CppInliner inliner{tempDirectory}; 120 | 121 | if (string(argv[2]) == "--prepare") { 122 | inliner.autoDetectCompilationOptions(); 123 | std::ofstream os(argv[3]); 124 | for (const auto& option : inliner.clangCompilationOptions) 125 | os << option << '\n'; 126 | return 0; 127 | } 128 | 129 | inliner.clangCompilationOptions = readNonEmptyLines(argv[2]); 130 | 131 | int numFailedTests = 0; 132 | for (int i = 3; i < argc; ++i) { 133 | try { 134 | if (!runTest(argv[i], tempDirectory, inliner)) { 135 | ++numFailedTests; 136 | } 137 | } catch (const std::exception& e) { 138 | ++numFailedTests; 139 | std::cout << e.what() << "\n"; 140 | } 141 | } 142 | 143 | return numFailedTests; 144 | } 145 | 146 | -------------------------------------------------------------------------------- /src/util.cpp: -------------------------------------------------------------------------------- 1 | // Caide C++ inliner 2 | // 3 | // This file is distributed under the GNU General Public License as published by 4 | // the Free Software Foundation, either version 3 of the License, or (at your 5 | // option) any later version. See LICENSE.TXT for details. 6 | 7 | #include "util.h" 8 | #include "clang_compat.h" 9 | #include "clang_version.h" 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | 22 | #include 23 | #include 24 | 25 | using namespace clang; 26 | 27 | namespace caide { 28 | namespace internal { 29 | 30 | // Copied from lib/ARCMigrate/Transforms.cpp 31 | 32 | /// \brief \arg Loc is the end of a statement range. This returns the location 33 | /// of the token of expected type following the statement. 34 | /// If no token of this type is found or the location is inside a macro, the returned 35 | /// source location will be invalid. 36 | SourceLocation findTokenAfterLocation(SourceLocation loc, ASTContext& Ctx, 37 | tok::TokenKind tokenType, bool IsDecl) 38 | { 39 | SourceManager &SM = Ctx.getSourceManager(); 40 | if (loc.isMacroID()) { 41 | if (!Lexer::isAtEndOfMacroExpansion(loc, SM, Ctx.getLangOpts(), &loc)) 42 | return SourceLocation(); 43 | } 44 | loc = Lexer::getLocForEndOfToken(loc, /*Offset=*/0, SM, Ctx.getLangOpts()); 45 | 46 | // Break down the source location. 47 | std::pair locInfo = SM.getDecomposedLoc(loc); 48 | 49 | // Try to load the file buffer. 50 | bool invalidTemp = false; 51 | StringRef file = SM.getBufferData(locInfo.first, &invalidTemp); 52 | if (invalidTemp) 53 | return SourceLocation(); 54 | 55 | const char *tokenBegin = file.data() + locInfo.second; 56 | 57 | // Lex from the start of the given location. 58 | Lexer lexer(SM.getLocForStartOfFile(locInfo.first), Ctx.getLangOpts(), 59 | file.begin(), tokenBegin, file.end()); 60 | Token tok; 61 | lexer.LexFromRawLexer(tok); 62 | if (tok.isNot(tokenType)) { 63 | if (!IsDecl) 64 | return SourceLocation(); 65 | // Declaration may be followed with other tokens; such as an __attribute, 66 | // before ending with a semicolon. 67 | return findTokenAfterLocation(tok.getLocation(), Ctx, tokenType, /*IsDecl*/true); 68 | } 69 | 70 | return tok.getLocation(); 71 | } 72 | 73 | SourceLocation findTokenAfterLocation(SourceLocation loc, ASTContext& Ctx, tok::TokenKind tokenType) { 74 | return findTokenAfterLocation(loc, Ctx, tokenType, false); 75 | } 76 | 77 | SourceLocation findSemiAfterLocation(SourceLocation loc, ASTContext& Ctx, bool IsDecl) { 78 | return findTokenAfterLocation(loc, Ctx, tok::semi, IsDecl); 79 | } 80 | 81 | /// \brief 'Loc' is the end of a statement range. This returns the location 82 | /// immediately after the semicolon following the statement. 83 | /// If no semicolon is found or the location is inside a macro, the returned 84 | /// source location will be invalid. 85 | SourceLocation findLocationAfterSemi(SourceLocation loc, ASTContext& Ctx, bool IsDecl) { 86 | SourceLocation SemiLoc = findSemiAfterLocation(loc, Ctx, IsDecl); 87 | if (SemiLoc.isInvalid()) 88 | return SourceLocation(); 89 | return SemiLoc.getLocWithOffset(1); 90 | } 91 | 92 | std::pair getCharRange(SourceRange range, const SourceManager& sourceManager, const LangOptions& langOpts) { 93 | #if CAIDE_CLANG_VERSION_AT_LEAST(7,0) 94 | CharSourceRange rng = sourceManager.getExpansionRange(range); 95 | #else 96 | range = sourceManager.getExpansionRange(range); 97 | // The following is actually incorrect because range could be a token range, 98 | // in which case we must use CharSourceRange::getTokenRange(). The only place 99 | // where we've encountered it so far is the range of #if condition with a newer clang, 100 | // which is covered by the other implementation above. 101 | // So for our use cases this should work. If we actually get token ranges from 102 | // older clang versions, we could copy the implementation of getExpansionRange 103 | // from latest clang. 104 | CharSourceRange rng = CharSourceRange::getCharRange(range); 105 | #endif 106 | rng = Lexer::makeFileCharRange(rng, sourceManager, langOpts); 107 | if (rng.isInvalid()) 108 | return {nullptr, nullptr}; 109 | bool Invalid = false; 110 | const char* b = sourceManager.getCharacterData(rng.getBegin(), &Invalid); 111 | if (Invalid) 112 | return {nullptr, nullptr}; 113 | const char* e = sourceManager.getCharacterData(rng.getEnd(), &Invalid); 114 | if (Invalid) 115 | return {nullptr, nullptr}; 116 | return {b, e}; 117 | } 118 | 119 | std::unique_ptr createCompilationDatabaseFromCommandLine(const std::vector& cmdLine) 120 | { 121 | int argc = (int)cmdLine.size() + 1; 122 | std::vector argv(argc); 123 | argv[0] = "--"; 124 | 125 | for (int i = 1; i < argc; ++i) 126 | argv[i] = cmdLine[i-1].c_str(); 127 | 128 | #if CAIDE_CLANG_VERSION_AT_LEAST(5,0) 129 | std::string errorMessage; 130 | return tooling::FixedCompilationDatabase::loadFromCommandLine(argc, &argv[0], errorMessage); 131 | #else 132 | tooling::FixedCompilationDatabase* rawPtr = tooling::FixedCompilationDatabase::loadFromCommandLine(argc, &argv[0]); 133 | return std::unique_ptr(rawPtr); 134 | #endif 135 | } 136 | 137 | std::string rangeToString(SourceManager& sourceManager, const SourceLocation& start, const SourceLocation& end) { 138 | bool invalid; 139 | const char* b = sourceManager.getCharacterData(start, &invalid); 140 | if (invalid || !b) 141 | return ""; 142 | const char* e = sourceManager.getCharacterData(end, &invalid); 143 | if (invalid || !e) 144 | return ""; 145 | if (e < b + 30) 146 | return std::string(b, e); 147 | else 148 | return std::string(b, b+30) + "[...]"; 149 | } 150 | 151 | std::string toString(SourceManager& sourceManager, SourceLocation loc) { 152 | //return loc.printToString(sourceManager); 153 | std::string fileName = sourceManager.getFilename(loc).str(); 154 | if (fileName.length() > 30) 155 | fileName = fileName.substr(fileName.length() - 30); 156 | std::ostringstream os; 157 | os << fileName << ":" << 158 | sourceManager.getExpansionLineNumber(loc) << ":" << 159 | sourceManager.getExpansionColumnNumber(loc); 160 | return os.str(); 161 | } 162 | 163 | #if CAIDE_CLANG_VERSION_AT_LEAST(7,0) 164 | static void debug(SourceManager& sourceManager, std::ostringstream& os, SourceLocation loc) { 165 | if (!loc.isValid()) os << "[invalid]"; 166 | else if (!loc.isFileID()) os << "[not file]"; 167 | else { 168 | std::string fileName = sourceManager.getFilename(loc).str(); 169 | if (fileName.length() > 30) 170 | fileName = fileName.substr(fileName.length() - 30); 171 | os << fileName << ":" << 172 | sourceManager.getSpellingLineNumber(loc) << ":" << 173 | sourceManager.getSpellingColumnNumber(loc); 174 | } 175 | } 176 | 177 | static void debug(SourceManager& sourceManager, std::ostringstream& os, CharSourceRange rng) { 178 | if (rng.isInvalid()) os << "{invalid range}"; 179 | else { 180 | os << "{"; 181 | if (rng.isTokenRange()) os << "token "; 182 | if (rng.isCharRange()) os << "char "; 183 | os << "range}"; 184 | debug(sourceManager, os, rng.getBegin()); 185 | os << "--"; 186 | debug(sourceManager, os, rng.getEnd()); 187 | } 188 | } 189 | 190 | std::string toString(SourceManager& sourceManager, SourceRange range, const LangOptions* langOpts /*=nullptr*/) { 191 | std::ostringstream os; 192 | CharSourceRange rng; 193 | rng = sourceManager.getExpansionRange(range); 194 | debug(sourceManager, os, rng); 195 | if (langOpts) { 196 | rng = Lexer::makeFileCharRange(rng, sourceManager, *langOpts); 197 | os << " "; 198 | debug(sourceManager, os, rng); 199 | } 200 | return os.str(); 201 | } 202 | 203 | #else 204 | std::string toString(SourceManager& sourceManager, SourceRange range, const LangOptions*) { 205 | return toString(sourceManager, range.getBegin()) + " -- " + 206 | toString(sourceManager, range.getEnd()); 207 | } 208 | #endif 209 | 210 | std::string toString(SourceManager& sourceManager, const Decl* decl) { 211 | if (!decl) 212 | return ""; 213 | SourceLocation start = sourceManager.getExpansionLoc(getBeginLoc(decl)); 214 | bool invalid; 215 | const char* b = sourceManager.getCharacterData(start, &invalid); 216 | if (invalid || !b) 217 | return ""; 218 | SourceLocation end = sourceManager.getExpansionLoc(getEndLoc(decl)); 219 | const char* e = sourceManager.getCharacterData(end, &invalid); 220 | if (invalid || !e) 221 | return ""; 222 | return std::string(b, std::min(b+30, e)); 223 | } 224 | 225 | std::string toString(ASTContext& ctx, const Stmt& stmt) { 226 | std::string res; 227 | llvm::raw_string_ostream os(res); 228 | #if CAIDE_CLANG_VERSION_AT_LEAST(11,0) 229 | stmt.dump(os, ctx); 230 | #else 231 | stmt.dump(os, ctx.getSourceManager()); 232 | #endif 233 | return res; 234 | } 235 | 236 | #if CAIDE_CLANG_VERSION_AT_LEAST(7,0) 237 | static SourceLocation getBegin(const CharSourceRange& charSourceRange) { 238 | return charSourceRange.getBegin(); 239 | } 240 | 241 | static SourceLocation getEnd(const CharSourceRange& charSourceRange) { 242 | return charSourceRange.getEnd(); 243 | } 244 | #else 245 | static SourceLocation getBegin(const std::pair& charSourceRange) { 246 | return charSourceRange.first; 247 | } 248 | 249 | static SourceLocation getEnd(const std::pair& charSourceRange) { 250 | return charSourceRange.second; 251 | } 252 | #endif 253 | 254 | SourceLocation getExpansionStart(SourceManager& sourceManager, const Decl* decl) { 255 | SourceLocation start = getBeginLoc(decl); 256 | if (start.isMacroID()) 257 | start = getBegin(sourceManager.getExpansionRange(start)); 258 | return start; 259 | } 260 | 261 | SourceLocation getExpansionEnd(SourceManager& sourceManager, const Decl* decl) { 262 | SourceLocation end = getEndLoc(decl); 263 | if (end.isMacroID()) 264 | end = getEnd(sourceManager.getExpansionRange(end)); 265 | return end; 266 | } 267 | 268 | SourceRange getExpansionRange(SourceManager& sourceManager, const Decl* decl) { 269 | return SourceRange(getExpansionStart(sourceManager, decl), 270 | getExpansionEnd(sourceManager, decl)); 271 | } 272 | 273 | } 274 | } 275 | 276 | -------------------------------------------------------------------------------- /src/util.h: -------------------------------------------------------------------------------- 1 | // Caide C++ inliner 2 | // 3 | // This file is distributed under the GNU General Public License as published by 4 | // the Free Software Foundation, either version 3 of the License, or (at your 5 | // option) any later version. See LICENSE.TXT for details. 6 | 7 | #pragma once 8 | 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | namespace clang { 17 | class ASTContext; 18 | class Decl; 19 | class LangOptions; 20 | class SourceLocation; 21 | class SourceManager; 22 | class SourceRange; 23 | class Stmt; 24 | 25 | namespace tooling { 26 | class FixedCompilationDatabase; 27 | } 28 | } 29 | 30 | namespace caide { 31 | namespace internal { 32 | 33 | clang::SourceLocation findTokenAfterLocation(clang::SourceLocation loc, clang::ASTContext& Ctx, clang::tok::TokenKind tokenType); 34 | clang::SourceLocation findSemiAfterLocation(clang::SourceLocation loc, clang::ASTContext& Ctx, bool IsDecl); 35 | clang::SourceLocation findLocationAfterSemi(clang::SourceLocation loc, clang::ASTContext& Ctx, bool IsDecl); 36 | 37 | std::pair getCharRange(clang::SourceRange range, const clang::SourceManager& SM, const clang::LangOptions& langOpts); 38 | 39 | std::unique_ptr createCompilationDatabaseFromCommandLine(const std::vector& cmdLine); 40 | 41 | std::string rangeToString(clang::SourceManager& sourceManager, 42 | const clang::SourceLocation& start, const clang::SourceLocation& end); 43 | 44 | std::string toString(clang::SourceManager& sourceManager, clang::SourceLocation loc); 45 | std::string toString(clang::SourceManager& sourceManager, clang::SourceRange range, const clang::LangOptions* = nullptr); 46 | std::string toString(clang::SourceManager& sourceManager, const clang::Decl* decl); 47 | std::string toString(clang::ASTContext&, const clang::Stmt&); 48 | 49 | clang::SourceLocation getExpansionStart(clang::SourceManager& sourceManager, 50 | const clang::Decl* decl); 51 | clang::SourceLocation getExpansionEnd(clang::SourceManager& sourceManager, 52 | const clang::Decl* decl); 53 | 54 | clang::SourceRange getExpansionRange(clang::SourceManager& sourceManager, 55 | const clang::Decl* decl); 56 | 57 | } 58 | } 59 | 60 | -------------------------------------------------------------------------------- /tests/cases/actually-written-type/1.cpp: -------------------------------------------------------------------------------- 1 | using UsedTypeAlias = int; 2 | 3 | template struct A {}; 4 | template<> struct A {}; 5 | 6 | typedef int usedTypedef; 7 | 8 | template void func() {} 9 | template<> void func() {} 10 | 11 | 12 | template struct B{}; 13 | 14 | template 15 | using UsedTemplateAlias = B; 16 | 17 | template struct C{}; 18 | template struct C> {}; 19 | 20 | 21 | template 22 | struct Traits { 23 | using type = T; 24 | }; 25 | 26 | template struct D{}; 27 | template<> struct D::type> {}; 28 | 29 | template 30 | struct Traits2 { 31 | using type = T; 32 | }; 33 | 34 | template struct E{}; 35 | template<> struct E::type> {}; 36 | 37 | int main() { 38 | A a; (void)a; 39 | func(); 40 | C> c; (void)c; 41 | D d; (void)d; 42 | E e; (void)e; 43 | } 44 | 45 | -------------------------------------------------------------------------------- /tests/cases/actually-written-type/clangOptions.txt: -------------------------------------------------------------------------------- 1 | -std=c++11 2 | 3 | -------------------------------------------------------------------------------- /tests/cases/actually-written-type/etalon.cpp: -------------------------------------------------------------------------------- 1 | using UsedTypeAlias = int; 2 | 3 | template struct A {}; 4 | template<> struct A {}; 5 | 6 | typedef int usedTypedef; 7 | 8 | template void func() {} 9 | template<> void func() {} 10 | 11 | 12 | template struct B{}; 13 | 14 | template 15 | using UsedTemplateAlias = B; 16 | 17 | template struct C{}; 18 | template struct C> {}; 19 | 20 | 21 | template 22 | struct Traits { 23 | using type = T; 24 | }; 25 | 26 | template struct D{}; 27 | template<> struct D::type> {}; 28 | 29 | template struct E{}; 30 | 31 | int main() { 32 | A a; (void)a; 33 | func(); 34 | C> c; (void)c; 35 | D d; (void)d; 36 | E e; (void)e; 37 | } 38 | 39 | -------------------------------------------------------------------------------- /tests/cases/alias-in-template-argument/1.cpp: -------------------------------------------------------------------------------- 1 | using X = int; 2 | using Y = int; 3 | 4 | template 5 | void f() { } 6 | 7 | template 8 | class C { 9 | }; 10 | 11 | int main() { 12 | f(); 13 | C* pc; 14 | } 15 | 16 | -------------------------------------------------------------------------------- /tests/cases/alias-in-template-argument/etalon.cpp: -------------------------------------------------------------------------------- 1 | using X = int; 2 | using Y = int; 3 | 4 | template 5 | void f() { } 6 | 7 | template 8 | class C { 9 | }; 10 | 11 | int main() { 12 | f(); 13 | C* pc; 14 | } 15 | 16 | -------------------------------------------------------------------------------- /tests/cases/base-class-of-template/1.cpp: -------------------------------------------------------------------------------- 1 | struct base {}; 2 | 3 | template 4 | struct templ : base {}; 5 | 6 | template <> 7 | struct templ {}; 8 | 9 | struct unused_base {}; 10 | template <> 11 | struct templ : unused_base {}; 12 | 13 | int main() { 14 | templ x; 15 | (void)x; 16 | } 17 | 18 | -------------------------------------------------------------------------------- /tests/cases/base-class-of-template/etalon.cpp: -------------------------------------------------------------------------------- 1 | struct base {}; 2 | 3 | template 4 | struct templ : base {}; 5 | 6 | template <> 7 | struct templ {}; 8 | 9 | int main() { 10 | templ x; 11 | (void)x; 12 | } 13 | 14 | -------------------------------------------------------------------------------- /tests/cases/base-initializers/1.cpp: -------------------------------------------------------------------------------- 1 | struct A {}; 2 | 3 | using Type = A; 4 | struct B: A { 5 | B(): Type() {} 6 | }; 7 | 8 | int main() { 9 | B b; 10 | (void)b; 11 | } 12 | 13 | -------------------------------------------------------------------------------- /tests/cases/base-initializers/clangOptions.txt: -------------------------------------------------------------------------------- 1 | -std=c++11 2 | 3 | -------------------------------------------------------------------------------- /tests/cases/base-initializers/etalon.cpp: -------------------------------------------------------------------------------- 1 | struct A {}; 2 | 3 | using Type = A; 4 | struct B: A { 5 | B(): Type() {} 6 | }; 7 | 8 | int main() { 9 | B b; 10 | (void)b; 11 | } 12 | 13 | -------------------------------------------------------------------------------- /tests/cases/caide-concept-comment/1.cpp: -------------------------------------------------------------------------------- 1 | class A { 2 | /// caide concept 3 | void f() {} 4 | void g() {} 5 | }; 6 | 7 | class B { 8 | /// caide concept 9 | void h() {} 10 | }; 11 | 12 | int main() { 13 | A a; 14 | (void)a; 15 | } 16 | -------------------------------------------------------------------------------- /tests/cases/caide-concept-comment/etalon.cpp: -------------------------------------------------------------------------------- 1 | class A { 2 | /// caide concept 3 | void f() {} 4 | }; 5 | 6 | int main() { 7 | A a; 8 | (void)a; 9 | } 10 | -------------------------------------------------------------------------------- /tests/cases/concepts-constraints/1.cpp: -------------------------------------------------------------------------------- 1 | template 2 | concept Add = requires(T a, T b) { 3 | { a + b }; 4 | }; 5 | 6 | template 7 | concept Subtract = requires(T a, T b) { 8 | { a - b }; 9 | }; 10 | 11 | template 12 | requires Add 13 | T add(T a, T b) { 14 | return a + b; 15 | } 16 | 17 | template 18 | concept Add2 = requires(T a, T b) { { a + b }; }; 19 | 20 | template 21 | T add2(T a) { return a; } 22 | 23 | template 24 | concept Add3 = requires(T a, T b) { { a + b }; }; 25 | 26 | auto add3(Add3 auto a) { return a; } 27 | 28 | template 29 | constexpr bool AlwaysTrue = true; 30 | 31 | template 32 | concept Concept4 = AlwaysTrue; 33 | 34 | namespace test5 { 35 | template 36 | constexpr bool AlwaysTrue = true; 37 | 38 | template 39 | requires AlwaysTrue 40 | void f(T) {} 41 | } 42 | 43 | int main() { 44 | add(-5, 5); 45 | add2(1); 46 | add3(1); 47 | Concept4 auto i = 1; 48 | test5::f(1); 49 | } 50 | 51 | -------------------------------------------------------------------------------- /tests/cases/concepts-constraints/clangOptions.txt: -------------------------------------------------------------------------------- 1 | -std=c++20 -------------------------------------------------------------------------------- /tests/cases/concepts-constraints/etalon.cpp: -------------------------------------------------------------------------------- 1 | template 2 | concept Add = requires(T a, T b) { 3 | { a + b }; 4 | }; 5 | 6 | template 7 | requires Add 8 | T add(T a, T b) { 9 | return a + b; 10 | } 11 | 12 | template 13 | concept Add2 = requires(T a, T b) { { a + b }; }; 14 | 15 | template 16 | T add2(T a) { return a; } 17 | 18 | template 19 | concept Add3 = requires(T a, T b) { { a + b }; }; 20 | 21 | auto add3(Add3 auto a) { return a; } 22 | 23 | template 24 | constexpr bool AlwaysTrue = true; 25 | 26 | template 27 | concept Concept4 = AlwaysTrue; 28 | 29 | namespace test5 { 30 | template 31 | constexpr bool AlwaysTrue = true; 32 | 33 | template 34 | requires AlwaysTrue 35 | void f(T) {} 36 | } 37 | 38 | int main() { 39 | add(-5, 5); 40 | add2(1); 41 | add3(1); 42 | Concept4 auto i = 1; 43 | test5::f(1); 44 | } 45 | 46 | -------------------------------------------------------------------------------- /tests/cases/delayed-parsing/1.cpp: -------------------------------------------------------------------------------- 1 | void unused() { } 2 | -------------------------------------------------------------------------------- /tests/cases/delayed-parsing/2.cpp: -------------------------------------------------------------------------------- 1 | template 2 | struct TC { 3 | const char* temp() { 4 | return "TC"; 5 | } 6 | }; 7 | 8 | template 9 | struct TC { 10 | const char* temp() { 11 | return "TC"; 12 | } 13 | }; 14 | 15 | template<> 16 | struct TC { 17 | const char* temp() { 18 | return "TC"; 19 | } 20 | }; 21 | 22 | template 23 | const char* func() { return "T"; } 24 | 25 | template<> 26 | const char* func() {return "int";} 27 | 28 | template<> 29 | const char* func() { return "int*"; } 30 | 31 | template 32 | void compilingFunc() { 33 | this is invalid syntax, but there should be no error because of delayed template parsing; 34 | }; 35 | 36 | 37 | int main() { 38 | TC ti; 39 | ti.temp(); 40 | TC tip; 41 | tip.temp(); 42 | 43 | func(); 44 | func(); 45 | } 46 | -------------------------------------------------------------------------------- /tests/cases/delayed-parsing/clangOptions.txt: -------------------------------------------------------------------------------- 1 | -fdelayed-template-parsing 2 | -------------------------------------------------------------------------------- /tests/cases/delayed-parsing/etalon.cpp: -------------------------------------------------------------------------------- 1 | template 2 | struct TC { 3 | }; 4 | 5 | template 6 | struct TC { 7 | const char* temp() { 8 | return "TC"; 9 | } 10 | }; 11 | 12 | template<> 13 | struct TC { 14 | const char* temp() { 15 | return "TC"; 16 | } 17 | }; 18 | 19 | template 20 | const char* func() { return "T"; } 21 | 22 | template<> 23 | const char* func() {return "int";} 24 | 25 | template<> 26 | const char* func() { return "int*"; } 27 | 28 | int main() { 29 | TC ti; 30 | ti.temp(); 31 | TC tip; 32 | tip.temp(); 33 | 34 | func(); 35 | func(); 36 | } 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /tests/cases/enums/1.cpp: -------------------------------------------------------------------------------- 1 | using Int1 = int; 2 | enum UnusedEnum { a, b, c}; 3 | enum class UnusedEnumClass { d, e, f}; 4 | 5 | using Int2 = int; 6 | constexpr int f() { return 10; } 7 | enum UsedEnum { g, h = f(), i}; 8 | enum class UsedEnumClass: Int2 { j, k, l}; 9 | 10 | int main() { 11 | (void)g; 12 | (void)UsedEnumClass::j; 13 | } 14 | 15 | -------------------------------------------------------------------------------- /tests/cases/enums/clangOptions.txt: -------------------------------------------------------------------------------- 1 | -std=c++11 2 | -------------------------------------------------------------------------------- /tests/cases/enums/etalon.cpp: -------------------------------------------------------------------------------- 1 | using Int2 = int; 2 | constexpr int f() { return 10; } 3 | enum UsedEnum { g, h = f(), i}; 4 | enum class UsedEnumClass: Int2 { j, k, l}; 5 | 6 | int main() { 7 | (void)g; 8 | (void)UsedEnumClass::j; 9 | } 10 | 11 | -------------------------------------------------------------------------------- /tests/cases/friends/1.cpp: -------------------------------------------------------------------------------- 1 | namespace case1 { 2 | 3 | class Class {}; 4 | template class ClassTemplate {}; 5 | template class AnotherClassTemplate {}; 6 | void func() {}; 7 | template void funcTemplate() {}; 8 | template void anotherFuncTemplate() {}; 9 | 10 | struct A { 11 | friend class Class; 12 | template friend class ClassTemplate; 13 | friend class AnotherClassTemplate; 14 | friend void func(); 15 | template friend void funcTemplate(); 16 | friend void anotherFuncTemplate(); 17 | }; 18 | void main() { A a; } 19 | 20 | } 21 | 22 | 23 | int main() { 24 | case1::main(); 25 | } 26 | 27 | -------------------------------------------------------------------------------- /tests/cases/friends/etalon.cpp: -------------------------------------------------------------------------------- 1 | namespace case1 { 2 | 3 | struct A { 4 | }; 5 | void main() { A a; } 6 | 7 | } 8 | 9 | 10 | int main() { 11 | case1::main(); 12 | } 13 | -------------------------------------------------------------------------------- /tests/cases/github-issue17/1.cpp: -------------------------------------------------------------------------------- 1 | template 2 | T value() { 3 | return 42; 4 | } 5 | 6 | typedef int Func(); 7 | 8 | int main() { 9 | Func* f = value; 10 | f(); 11 | } 12 | 13 | -------------------------------------------------------------------------------- /tests/cases/github-issue17/etalon.cpp: -------------------------------------------------------------------------------- 1 | template 2 | T value() { 3 | return 42; 4 | } 5 | 6 | typedef int Func(); 7 | 8 | int main() { 9 | Func* f = value; 10 | f(); 11 | } 12 | 13 | -------------------------------------------------------------------------------- /tests/cases/github-issue4/1.cpp: -------------------------------------------------------------------------------- 1 | template class A; 2 | 3 | template 4 | class A { 5 | public: 6 | template 7 | void f(U) {} 8 | }; 9 | 10 | int main() { 11 | A a; 12 | a.f(1); 13 | } 14 | 15 | -------------------------------------------------------------------------------- /tests/cases/github-issue4/etalon.cpp: -------------------------------------------------------------------------------- 1 | template class A; 2 | 3 | template 4 | class A { 5 | public: 6 | template 7 | void f(U) {} 8 | }; 9 | 10 | int main() { 11 | A a; 12 | a.f(1); 13 | } 14 | 15 | -------------------------------------------------------------------------------- /tests/cases/github-issue8/1.cpp: -------------------------------------------------------------------------------- 1 | struct S1 { using type = int; }; 2 | 3 | template 4 | void f1() { } 5 | 6 | struct S2 { 7 | using type = int; 8 | using type2 = int; 9 | }; 10 | 11 | template 12 | void f2(T2, typename T2::type2 x) { } 13 | 14 | template 15 | struct S3 { 16 | using type = T; 17 | }; 18 | 19 | template 20 | void f3(T&) { } 21 | 22 | template 23 | struct S4 { using type = T; }; 24 | 25 | template 26 | class C4 {}; 27 | 28 | template 29 | struct S5 { using type = T; }; 30 | 31 | template 32 | using C5 = T; 33 | 34 | template 35 | struct S6 { using type = void; }; 36 | 37 | template 38 | struct C6 {}; 39 | 40 | template 41 | struct C6::type> { }; 42 | 43 | template 44 | class C7 { 45 | template 46 | using X1 = int; 47 | 48 | template 49 | using X2 = X1; 50 | }; 51 | 52 | template 53 | class C7> { 54 | template 55 | using X1 = int; 56 | 57 | template 58 | using X2 = X1; 59 | }; 60 | 61 | template <> 62 | class C7 { 63 | template 64 | class C { 65 | template 66 | using X1 = int; 67 | 68 | template 69 | using X2 = X1; 70 | }; 71 | 72 | template 73 | void f() { 74 | using X1 = int; 75 | using X2 = X1; 76 | } 77 | }; 78 | 79 | template <> 80 | void C7::f() { 81 | using X1 = int; 82 | using X2 = X1; 83 | } 84 | 85 | namespace test8 { 86 | template 87 | constexpr bool AlwaysTrue = true; 88 | 89 | template 90 | struct Foo { 91 | }; 92 | 93 | template <> 94 | struct Foo { 95 | using type = int; 96 | }; 97 | 98 | template ::type> 99 | requires AlwaysTrue 100 | struct Bar 101 | { 102 | }; 103 | 104 | int main() { 105 | Bar x; 106 | return 0; 107 | } 108 | } 109 | 110 | namespace test9 { 111 | template 112 | constexpr bool AlwaysTrue = true; 113 | 114 | template 115 | struct S1 { }; 116 | 117 | template <> 118 | struct S1 { 119 | using type = int; 120 | }; 121 | 122 | template 123 | struct Foo { }; 124 | 125 | template 126 | requires AlwaysTrue::type> 127 | struct Foo 128 | { }; 129 | 130 | int main() { 131 | Foo x; 132 | return 0; 133 | } 134 | } 135 | 136 | namespace test10 { 137 | template 138 | struct S1 { }; 139 | 140 | template <> 141 | struct S1 { 142 | using type = long; 143 | }; 144 | 145 | template ::type V> 146 | struct S2 147 | { }; 148 | 149 | int main() { 150 | S2 x; 151 | return 0; 152 | } 153 | } 154 | 155 | namespace test11 { 156 | template 157 | struct S1{}; 158 | 159 | template<> 160 | struct S1 { 161 | constexpr static bool value = true; 162 | }; 163 | 164 | template 165 | concept Concept1 = S1::value; 166 | 167 | template 168 | void f() 169 | { 170 | Concept1 auto x = 1.0; 171 | } 172 | 173 | int main() { 174 | f(); 175 | return 0; 176 | } 177 | } 178 | 179 | namespace test12 { 180 | template 181 | struct Foo { 182 | }; 183 | 184 | template <> 185 | struct Foo { 186 | using type = int; 187 | }; 188 | 189 | template 190 | struct Bar { 191 | }; 192 | 193 | template <> 194 | struct Bar { 195 | using type = int; 196 | }; 197 | 198 | template ::type, typename V = Bar::type> 199 | struct S {}; 200 | 201 | int main() { 202 | S(); 203 | return 0; 204 | } 205 | } 206 | 207 | namespace test13 { 208 | template 209 | struct Foo { 210 | }; 211 | 212 | template <> 213 | struct Foo { 214 | using type = int; 215 | }; 216 | 217 | template 218 | struct Bar { 219 | }; 220 | 221 | template <> 222 | struct Bar { 223 | using type = int; 224 | }; 225 | 226 | template ::type, typename V = Bar::type> 227 | using S = T; 228 | 229 | int main() { 230 | S x; 231 | (void)x; 232 | return 0; 233 | } 234 | } 235 | 236 | namespace test14 { 237 | template 238 | struct S { 239 | using type = long; 240 | }; 241 | 242 | template ::type V> 243 | concept C = true; 244 | 245 | int main() { 246 | C auto x = 1; 247 | return 0; 248 | } 249 | } 250 | 251 | namespace test15 { 252 | template 253 | struct S { 254 | using type = long; 255 | }; 256 | 257 | template ::type> 258 | concept C = true; 259 | 260 | int main() { 261 | C auto x = 1; 262 | return 0; 263 | } 264 | } 265 | 266 | int main() { 267 | f1(); 268 | f2(S2{}, 0); 269 | 270 | S3 s3; 271 | f3(s3); 272 | 273 | C4> c4; 274 | C5> c5; 275 | C6 c6; 276 | test8::main(); 277 | test9::main(); 278 | test10::main(); 279 | test11::main(); 280 | test12::main(); 281 | test13::main(); 282 | test14::main(); 283 | test15::main(); 284 | } 285 | -------------------------------------------------------------------------------- /tests/cases/github-issue8/clangOptions.txt: -------------------------------------------------------------------------------- 1 | -std=c++20 2 | -------------------------------------------------------------------------------- /tests/cases/github-issue8/etalon.cpp: -------------------------------------------------------------------------------- 1 | struct S1 { using type = int; }; 2 | 3 | template 4 | void f1() { } 5 | 6 | struct S2 { 7 | using type = int; 8 | using type2 = int; 9 | }; 10 | 11 | template 12 | void f2(T2, typename T2::type2 x) { } 13 | 14 | template 15 | struct S3 { 16 | using type = T; 17 | }; 18 | 19 | template 20 | void f3(T&) { } 21 | 22 | template 23 | struct S4 { using type = T; }; 24 | 25 | template 26 | class C4 {}; 27 | 28 | template 29 | struct S5 { using type = T; }; 30 | 31 | template 32 | using C5 = T; 33 | 34 | template 35 | struct S6 { using type = void; }; 36 | 37 | template 38 | struct C6 {}; 39 | 40 | template 41 | struct C6::type> { }; 42 | 43 | namespace test8 { 44 | template 45 | constexpr bool AlwaysTrue = true; 46 | 47 | template 48 | struct Foo { 49 | }; 50 | 51 | template <> 52 | struct Foo { 53 | using type = int; 54 | }; 55 | 56 | template ::type> 57 | requires AlwaysTrue 58 | struct Bar 59 | { 60 | }; 61 | 62 | int main() { 63 | Bar x; 64 | return 0; 65 | } 66 | } 67 | 68 | namespace test9 { 69 | template 70 | constexpr bool AlwaysTrue = true; 71 | 72 | template 73 | struct S1 { }; 74 | 75 | template <> 76 | struct S1 { 77 | using type = int; 78 | }; 79 | 80 | template 81 | struct Foo { }; 82 | 83 | template 84 | requires AlwaysTrue::type> 85 | struct Foo 86 | { }; 87 | 88 | int main() { 89 | Foo x; 90 | return 0; 91 | } 92 | } 93 | 94 | namespace test10 { 95 | template 96 | struct S1 { }; 97 | 98 | template <> 99 | struct S1 { 100 | using type = long; 101 | }; 102 | 103 | template ::type V> 104 | struct S2 105 | { }; 106 | 107 | int main() { 108 | S2 x; 109 | return 0; 110 | } 111 | } 112 | 113 | namespace test11 { 114 | template 115 | struct S1{}; 116 | 117 | template<> 118 | struct S1 { 119 | constexpr static bool value = true; 120 | }; 121 | 122 | template 123 | concept Concept1 = S1::value; 124 | 125 | template 126 | void f() 127 | { 128 | Concept1 auto x = 1.0; 129 | } 130 | 131 | int main() { 132 | f(); 133 | return 0; 134 | } 135 | } 136 | 137 | namespace test12 { 138 | template 139 | struct Foo { 140 | }; 141 | 142 | template <> 143 | struct Foo { 144 | using type = int; 145 | }; 146 | 147 | template 148 | struct Bar { 149 | }; 150 | 151 | template <> 152 | struct Bar { 153 | using type = int; 154 | }; 155 | 156 | template ::type, typename V = Bar::type> 157 | struct S {}; 158 | 159 | int main() { 160 | S(); 161 | return 0; 162 | } 163 | } 164 | 165 | namespace test13 { 166 | template 167 | struct Foo { 168 | }; 169 | 170 | template <> 171 | struct Foo { 172 | using type = int; 173 | }; 174 | 175 | template 176 | struct Bar { 177 | }; 178 | 179 | template <> 180 | struct Bar { 181 | using type = int; 182 | }; 183 | 184 | template ::type, typename V = Bar::type> 185 | using S = T; 186 | 187 | int main() { 188 | S x; 189 | (void)x; 190 | return 0; 191 | } 192 | } 193 | 194 | namespace test14 { 195 | template 196 | struct S { 197 | using type = long; 198 | }; 199 | 200 | template ::type V> 201 | concept C = true; 202 | 203 | int main() { 204 | C auto x = 1; 205 | return 0; 206 | } 207 | } 208 | 209 | namespace test15 { 210 | template 211 | struct S { 212 | using type = long; 213 | }; 214 | 215 | template ::type> 216 | concept C = true; 217 | 218 | int main() { 219 | C auto x = 1; 220 | return 0; 221 | } 222 | } 223 | 224 | int main() { 225 | f1(); 226 | f2(S2{}, 0); 227 | 228 | S3 s3; 229 | f3(s3); 230 | 231 | C4> c4; 232 | C5> c5; 233 | C6 c6; 234 | test8::main(); 235 | test9::main(); 236 | test10::main(); 237 | test11::main(); 238 | test12::main(); 239 | test13::main(); 240 | test14::main(); 241 | test15::main(); 242 | } 243 | -------------------------------------------------------------------------------- /tests/cases/ident-to-keep/1.cpp: -------------------------------------------------------------------------------- 1 | void f1() { } 2 | void f2() { } 3 | void f3() { } 4 | 5 | namespace ns { 6 | class C { 7 | C(int) { f1(); } 8 | void method1() { 9 | f2(); 10 | } 11 | 12 | void method2() { } 13 | }; 14 | } 15 | 16 | int main() { 17 | } 18 | 19 | -------------------------------------------------------------------------------- /tests/cases/ident-to-keep/etalon.cpp: -------------------------------------------------------------------------------- 1 | void f1() { } 2 | void f2() { } 3 | 4 | namespace ns { 5 | class C { 6 | C(int) { f1(); } 7 | void method1() { 8 | f2(); 9 | } 10 | 11 | }; 12 | } 13 | 14 | int main() { 15 | } 16 | 17 | 18 | -------------------------------------------------------------------------------- /tests/cases/ident-to-keep/identifiersToKeep.txt: -------------------------------------------------------------------------------- 1 | ns::C::C 2 | ns::C::method1 3 | -------------------------------------------------------------------------------- /tests/cases/include-option-std/1.cpp: -------------------------------------------------------------------------------- 1 | int main() { 2 | in_std(); 3 | return 0; 4 | } 5 | -------------------------------------------------------------------------------- /tests/cases/include-option-std/clangOptions.txt: -------------------------------------------------------------------------------- 1 | -isystem 2 | TEST_ROOT/mystd 3 | -include 4 | mystd.h -------------------------------------------------------------------------------- /tests/cases/include-option-std/etalon.cpp: -------------------------------------------------------------------------------- 1 | int main() { 2 | in_std(); 3 | return 0; 4 | } 5 | -------------------------------------------------------------------------------- /tests/cases/include-option-std/mystd/mystd.h: -------------------------------------------------------------------------------- 1 | void in_std() { } -------------------------------------------------------------------------------- /tests/cases/include-option-user/1.cpp: -------------------------------------------------------------------------------- 1 | int main() { 2 | in_user_header(); 3 | return 0; 4 | } 5 | -------------------------------------------------------------------------------- /tests/cases/include-option-user/clangOptions.txt: -------------------------------------------------------------------------------- 1 | -I 2 | TEST_ROOT 3 | -include 4 | user_header.h -------------------------------------------------------------------------------- /tests/cases/include-option-user/etalon.cpp: -------------------------------------------------------------------------------- 1 | void in_user_header() {} 2 | int main() { 3 | in_user_header(); 4 | return 0; 5 | } 6 | -------------------------------------------------------------------------------- /tests/cases/include-option-user/user_header.h: -------------------------------------------------------------------------------- 1 | void in_user_header() {} 2 | -------------------------------------------------------------------------------- /tests/cases/inheriting-ctor/1.cpp: -------------------------------------------------------------------------------- 1 | struct A { 2 | A(int) {} 3 | }; 4 | 5 | struct B : A { 6 | using A::A; 7 | }; 8 | 9 | int main() { 10 | B(52); 11 | return 0; 12 | } 13 | -------------------------------------------------------------------------------- /tests/cases/inheriting-ctor/clangOptions.txt: -------------------------------------------------------------------------------- 1 | -std=c++11 2 | -------------------------------------------------------------------------------- /tests/cases/inheriting-ctor/etalon.cpp: -------------------------------------------------------------------------------- 1 | struct A { 2 | A(int) {} 3 | }; 4 | 5 | struct B : A { 6 | using A::A; 7 | }; 8 | 9 | int main() { 10 | B(52); 11 | return 0; 12 | } 13 | -------------------------------------------------------------------------------- /tests/cases/inliner1/1.cpp: -------------------------------------------------------------------------------- 1 | void unused() { } 2 | -------------------------------------------------------------------------------- /tests/cases/inliner1/2.cpp: -------------------------------------------------------------------------------- 1 | template 2 | struct TC { 3 | const char* temp() { 4 | return "TC"; 5 | } 6 | }; 7 | 8 | template 9 | struct TC { 10 | const char* temp() { 11 | return "TC"; 12 | } 13 | }; 14 | 15 | template<> 16 | struct TC { 17 | const char* temp() { 18 | return "TC"; 19 | } 20 | }; 21 | 22 | template 23 | const char* func() { return "T"; } 24 | 25 | template<> 26 | const char* func() {return "int";} 27 | 28 | template<> 29 | const char* func() { return "int*"; } 30 | 31 | #define MULTILINE \ 32 | f(); \ 33 | g(); 34 | 35 | #define DEF_C 'a' 36 | #define DEF_I 0 37 | #define DEF_I2 2 38 | #define DEF_I3 3 39 | 40 | int unused_func() { 41 | return DEF_I3; 42 | } 43 | 44 | int unused_func2() { 45 | #ifdef SOMETHING 46 | return 1; 47 | #else 48 | return 2; 49 | #endif 50 | } 51 | 52 | #define UNUSED_MACRO1 void f(){\ 53 | } 54 | 55 | template 56 | class TemplateMethodInTemplateClass { 57 | public: 58 | template 59 | TemplateMethodInTemplateClass(It a, It b) {} 60 | 61 | template 62 | void print(It a) {} 63 | }; 64 | 65 | int main() { 66 | TC ti; 67 | ti.temp(); 68 | TC tip; 69 | tip.temp(); 70 | 71 | func(); 72 | func(); 73 | 74 | if (true) 75 | ; 76 | 77 | TemplateMethodInTemplateClass c(1, 2); 78 | c.print(3); 79 | 80 | #if 1 > 2 81 | ti.temp(); 82 | return DEF_I2; 83 | #elif 1 > 3 84 | tip.temp(); 85 | #elif defined(FOO) 86 | func(); 87 | #endif 88 | 89 | #define FOO 90 | 91 | #ifndef FOO 92 | ti.temp(); 93 | #endif 94 | 95 | #ifdef BAR 96 | ti.temp(); 97 | #else 98 | # ifdef FOO 99 | if (*ti.temp() == DEF_C) return DEF_I; 100 | # else 101 | ti.temp(); 102 | # endif 103 | #endif 104 | 105 | #ifdef ONLINE_JUDGE 106 | ti.temp(); 107 | #else 108 | tip.temp(); 109 | #endif 110 | 111 | #if 1 < 2 112 | ti.temp(); 113 | #elif defined(ONLINE_JUDGE) 114 | tip.temp(); 115 | #endif 116 | }; 117 | #undef DEF_I 118 | 119 | -------------------------------------------------------------------------------- /tests/cases/inliner1/etalon.cpp: -------------------------------------------------------------------------------- 1 | template 2 | struct TC { 3 | }; 4 | 5 | template 6 | struct TC { 7 | const char* temp() { 8 | return "TC"; 9 | } 10 | }; 11 | 12 | template<> 13 | struct TC { 14 | const char* temp() { 15 | return "TC"; 16 | } 17 | }; 18 | 19 | template 20 | const char* func() { return "T"; } 21 | 22 | template<> 23 | const char* func() {return "int";} 24 | 25 | template<> 26 | const char* func() { return "int*"; } 27 | 28 | #define DEF_C 'a' 29 | #define DEF_I 0 30 | 31 | template 32 | class TemplateMethodInTemplateClass { 33 | public: 34 | template 35 | TemplateMethodInTemplateClass(It a, It b) {} 36 | 37 | template 38 | void print(It a) {} 39 | }; 40 | 41 | int main() { 42 | TC ti; 43 | ti.temp(); 44 | TC tip; 45 | tip.temp(); 46 | 47 | func(); 48 | func(); 49 | 50 | if (true) 51 | ; 52 | 53 | TemplateMethodInTemplateClass c(1, 2); 54 | c.print(3); 55 | 56 | 57 | 58 | 59 | 60 | 61 | if (*ti.temp() == DEF_C) return DEF_I; 62 | 63 | #ifdef ONLINE_JUDGE 64 | ti.temp(); 65 | #else 66 | tip.temp(); 67 | #endif 68 | 69 | #if 1 < 2 70 | ti.temp(); 71 | #elif defined(ONLINE_JUDGE) 72 | tip.temp(); 73 | #endif 74 | 75 | } 76 | 77 | #undef DEF_I 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /tests/cases/inliner1/macrosToKeep.txt: -------------------------------------------------------------------------------- 1 | ONLINE_JUDGE 2 | -------------------------------------------------------------------------------- /tests/cases/inliner2/1.cpp: -------------------------------------------------------------------------------- 1 | void unused() { } 2 | -------------------------------------------------------------------------------- /tests/cases/inliner2/2.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace spcppl { 4 | 5 | template 6 | double f(T& t) { 7 | return double(t); 8 | } 9 | 10 | } // namespace spcppl 11 | 12 | class X { 13 | public: 14 | void f() const { 15 | } 16 | }; 17 | 18 | template 19 | void goF(const T& t) { 20 | t.f(); 21 | } 22 | 23 | class Vertex { 24 | public: 25 | Vertex() {} 26 | Vertex(int a, int b) {} 27 | }; 28 | 29 | struct Y { 30 | bool operator ++ (int x) { 31 | return true; 32 | } 33 | int operator() (int x) { 34 | return 0; 35 | } 36 | }; 37 | 38 | bool operator + (Y a, Y b) { 39 | return true; 40 | } 41 | 42 | struct Used { 43 | int x; 44 | 45 | ~Used() { 46 | x = 2; 47 | } 48 | }; 49 | 50 | struct Unused { 51 | int x; 52 | ~Unused() { 53 | x = 3; 54 | } 55 | }; 56 | 57 | struct UsedTypedefedStruct { 58 | int a; 59 | }; 60 | 61 | typedef UsedTypedefedStruct UsedTypedef; 62 | 63 | struct UnusedTypedefedStruct { 64 | int a; 65 | }; 66 | 67 | typedef UnusedTypedefedStruct UnusedTypedef; 68 | typedef UsedTypedefedStruct UnusedTypedef2; 69 | 70 | struct UsedTypedefedStruct2 { 71 | int a; 72 | }; 73 | 74 | typedef UsedTypedefedStruct2 UsedTypedef2; 75 | typedef UsedTypedef2 RecursiveUsedTypedef2; 76 | 77 | void unused_global_func() {} 78 | 79 | struct UsedCall { 80 | static void staticCall() {} 81 | }; 82 | 83 | template 84 | struct V { 85 | T t; 86 | }; 87 | 88 | template 89 | struct V2 { 90 | T t; 91 | }; 92 | 93 | 94 | template 95 | struct V3 { 96 | T t; 97 | }; 98 | 99 | 100 | struct S1 { 101 | typedef int type; 102 | }; 103 | 104 | template 105 | struct S2 { 106 | typedef T type; 107 | }; 108 | 109 | template 110 | struct S3 { 111 | typedef T type; 112 | static type get() { 113 | return type(); 114 | } 115 | }; 116 | 117 | struct S4 { 118 | typedef V2 type; 119 | }; 120 | 121 | struct S5 { 122 | typedef V3 type; 123 | }; 124 | 125 | template 126 | struct S6 { 127 | }; 128 | 129 | struct S7 {}; 130 | 131 | template 132 | struct IntTemplate { 133 | static const int val = n*n; 134 | }; 135 | 136 | typedef long long ll; 137 | 138 | #include "inc.h" 139 | 140 | int main() { 141 | { 142 | int x; 143 | spcppl::f(x); 144 | goF(X()); 145 | } 146 | 147 | { 148 | mystd::vector v; 149 | v.resize(100); 150 | } 151 | 152 | { 153 | Y y; 154 | y++; 155 | y + y; 156 | } 157 | 158 | { 159 | Used used; 160 | } 161 | 162 | { 163 | UsedTypedef f; 164 | RecursiveUsedTypedef2 g; 165 | } 166 | 167 | { 168 | mystd::vector::callIntoTemplate(); 169 | } 170 | 171 | { 172 | S1::type i; 173 | auto v = S3 >::get(); 174 | S4::type v2; 175 | } 176 | 177 | { 178 | S6 s6; 179 | S6 s6_s7; 180 | } 181 | 182 | { 183 | IntTemplate<4>::val; 184 | } 185 | 186 | { 187 | typedef int ta; 188 | typedef int tb; 189 | ta i = 2; 190 | struct LocalUsed { 191 | typedef int tc; 192 | void used() {} 193 | void unused() {} 194 | } lu; 195 | lu.used(); 196 | struct LocalUnused {}; 197 | } 198 | 199 | { 200 | struct Outer { 201 | struct Inner {}; 202 | }; 203 | Outer::Inner inner; 204 | } 205 | 206 | return 0; 207 | } 208 | 209 | -------------------------------------------------------------------------------- /tests/cases/inliner2/clangOptions.txt: -------------------------------------------------------------------------------- 1 | -std=c++11 2 | -isystem 3 | TEST_ROOT/system-inc 4 | -I 5 | TEST_ROOT/user-inc 6 | -------------------------------------------------------------------------------- /tests/cases/inliner2/etalon.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace spcppl { 4 | 5 | template 6 | double f(T& t) { 7 | return double(t); 8 | } 9 | 10 | } // namespace spcppl 11 | 12 | class X { 13 | public: 14 | void f() const { 15 | } 16 | }; 17 | 18 | template 19 | void goF(const T& t) { 20 | t.f(); 21 | } 22 | 23 | class Vertex { 24 | public: 25 | Vertex() {} 26 | 27 | }; 28 | 29 | struct Y { 30 | bool operator ++ (int x) { 31 | return true; 32 | } 33 | 34 | }; 35 | 36 | bool operator + (Y a, Y b) { 37 | return true; 38 | } 39 | 40 | struct Used { 41 | int x; 42 | 43 | ~Used() { 44 | x = 2; 45 | } 46 | }; 47 | 48 | 49 | struct UsedTypedefedStruct { 50 | 51 | }; 52 | 53 | typedef UsedTypedefedStruct UsedTypedef; 54 | 55 | 56 | struct UsedTypedefedStruct2 { 57 | 58 | }; 59 | 60 | typedef UsedTypedefedStruct2 UsedTypedef2; 61 | typedef UsedTypedef2 RecursiveUsedTypedef2; 62 | 63 | 64 | struct UsedCall { 65 | static void staticCall() {} 66 | }; 67 | 68 | template 69 | struct V { 70 | T t; 71 | }; 72 | 73 | template 74 | struct V2 { 75 | 76 | }; 77 | 78 | 79 | struct S1 { 80 | typedef int type; 81 | }; 82 | 83 | 84 | template 85 | struct S3 { 86 | typedef T type; 87 | static type get() { 88 | return type(); 89 | } 90 | }; 91 | 92 | struct S4 { 93 | typedef V2 type; 94 | }; 95 | 96 | 97 | template 98 | struct S6 { 99 | }; 100 | 101 | struct S7 {}; 102 | 103 | template 104 | struct IntTemplate { 105 | static const int val = n*n; 106 | }; 107 | 108 | typedef long long ll; 109 | 110 | 111 | int main() { 112 | { 113 | int x; 114 | spcppl::f(x); 115 | goF(X()); 116 | } 117 | 118 | { 119 | mystd::vector v; 120 | v.resize(100); 121 | } 122 | 123 | { 124 | Y y; 125 | y++; 126 | y + y; 127 | } 128 | 129 | { 130 | Used used; 131 | } 132 | 133 | { 134 | UsedTypedef f; 135 | RecursiveUsedTypedef2 g; 136 | } 137 | 138 | { 139 | mystd::vector::callIntoTemplate(); 140 | } 141 | 142 | { 143 | S1::type i; 144 | auto v = S3 >::get(); 145 | S4::type v2; 146 | } 147 | 148 | { 149 | S6 s6; 150 | S6 s6_s7; 151 | } 152 | 153 | { 154 | IntTemplate<4>::val; 155 | } 156 | 157 | { 158 | typedef int ta; 159 | 160 | ta i = 2; 161 | struct LocalUsed { 162 | 163 | void used() {} 164 | 165 | } lu; 166 | lu.used(); 167 | 168 | } 169 | 170 | { 171 | struct Outer { 172 | struct Inner {}; 173 | }; 174 | Outer::Inner inner; 175 | } 176 | 177 | return 0; 178 | } 179 | 180 | 181 | -------------------------------------------------------------------------------- /tests/cases/inliner2/system-inc/myvector: -------------------------------------------------------------------------------- 1 | #pragma once 2 | namespace mystd { 3 | template 4 | class vector { 5 | T* arr; 6 | int size; 7 | public: 8 | vector() 9 | : arr(0) 10 | , size(0) 11 | {} 12 | void resize(int newSize) { 13 | if (newSize > size) { 14 | T* newArr = new T[newSize]; 15 | for (int i = 0; i < size; ++i) 16 | newArr[i] = arr[i]; 17 | delete[] arr; 18 | arr = newArr; 19 | size = newSize; 20 | } 21 | } 22 | 23 | static void callIntoTemplate() { 24 | T::staticCall(); 25 | } 26 | }; 27 | } 28 | 29 | -------------------------------------------------------------------------------- /tests/cases/inliner2/user-inc/inc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | -------------------------------------------------------------------------------- /tests/cases/inliner3/1.cpp: -------------------------------------------------------------------------------- 1 | void unused() { } 2 | -------------------------------------------------------------------------------- /tests/cases/inliner3/2.cpp: -------------------------------------------------------------------------------- 1 | void f1() {} 2 | void f2() { 3 | typedef int ii; 4 | f1(); 5 | } 6 | 7 | int gen() { 8 | static int c = 0; 9 | return ++c; 10 | } 11 | 12 | void f4() { 13 | struct S1 { 14 | void f5() { 15 | } 16 | }; 17 | S1 x; 18 | static int i = gen(); 19 | } 20 | 21 | 22 | template 23 | struct Outer 24 | { 25 | struct Inner 26 | { 27 | void unused() {} 28 | }; 29 | 30 | Inner inner; 31 | }; 32 | 33 | template 34 | void f3() { 35 | Outer v; 36 | (void)v.inner; 37 | } 38 | 39 | int f6() { 40 | return 6; 41 | } 42 | 43 | struct S2 { 44 | int i; 45 | S2() 46 | : i(f6()) 47 | {} 48 | }; 49 | 50 | int f7() { 51 | return 7; 52 | } 53 | 54 | void f8(int i = f7()) { 55 | } 56 | 57 | /// caide keep 58 | void f9() {} 59 | 60 | int i1, i2 = 0, i3, i4 , i5; 61 | 62 | int i7, i8, i9, i10; 63 | 64 | #define td int 65 | #define I1 5 66 | 67 | td v1[I1], v2; 68 | 69 | #define td2 int 70 | #define I2 5 71 | 72 | td2 v3[I2]; 73 | 74 | 75 | #define foo int 76 | #define bar foo 77 | void usedFunc() {} 78 | bar y() { return 1; } 79 | 80 | typedef double db; 81 | db dp[100]; 82 | 83 | struct A { 84 | static const int x = 2; 85 | }; 86 | typedef A VI; 87 | struct B : VI { 88 | }; 89 | 90 | typedef double DD; 91 | 92 | typedef A atd1; 93 | typedef A atd2; 94 | typedef A atd3; 95 | typedef A atd4; 96 | 97 | void f(atd3& a){} 98 | 99 | template 100 | struct S3 { 101 | static void fill(); 102 | }; 103 | 104 | template 105 | void S3::fill() { 106 | } 107 | 108 | template<> 109 | void S3::fill() { 110 | } 111 | 112 | struct S4 { 113 | S4(int a, int b){} 114 | }; 115 | 116 | typedef S4 tds4; 117 | 118 | template 119 | struct Identity { typedef T type; }; 120 | 121 | typedef Identity::type inttd; 122 | 123 | template 124 | void noopFunc(T t) {} 125 | 126 | 127 | template 128 | struct UnusedClass { 129 | template 130 | void method(); 131 | }; 132 | 133 | template 134 | template 135 | void UnusedClass::method() { 136 | } 137 | 138 | void caide_keep_inside_unused_function() { 139 | /// caide keep 140 | typedef int TD; 141 | } 142 | 143 | template 144 | void caide_keep_inside_unused_function_template() { 145 | /// caide keep 146 | typedef int TD; 147 | } 148 | 149 | template 150 | void forwaredDeclared(); 151 | 152 | class DefaultTypeParam {}; 153 | 154 | template 155 | class WithDefaultTypeParam 156 | { 157 | }; 158 | 159 | struct C{ 160 | template 161 | void f(S s); 162 | }; 163 | 164 | template 165 | void C::f(S s) { 166 | } 167 | 168 | template 169 | struct D { 170 | template 171 | void f() {} 172 | 173 | template 174 | void g(); 175 | }; 176 | 177 | template 178 | template 179 | void D::g() { 180 | } 181 | 182 | template 183 | void usedFunc1(); 184 | 185 | 186 | template 187 | void usedFunc1(); 188 | 189 | template 190 | void usedFunc1() { 191 | } 192 | 193 | template 194 | struct E{}; 195 | 196 | template > 197 | struct F {}; 198 | 199 | struct G { 200 | void used() {} 201 | G() = default; 202 | G(const G&) = default; 203 | G& operator=(const G&) = delete; 204 | ~G() { 205 | used(); 206 | } 207 | }; 208 | 209 | template 210 | struct H { 211 | template 212 | using UsedAlias = int; 213 | 214 | template 215 | using UnusedAlias = int; 216 | }; 217 | 218 | template 219 | struct UsedThroughAlias { 220 | }; 221 | 222 | template 223 | using StructAlias = UsedThroughAlias; 224 | 225 | template 226 | using UnusedStructAlias = UsedThroughAlias; 227 | 228 | int main() { 229 | f2(); 230 | //f3(); 231 | f4(); 232 | S2 s2; 233 | f8(); 234 | i4 = i8 = i10 = 1; 235 | usedFunc(); 236 | v2 = 1; 237 | dp[0] = 1; 238 | B b; 239 | dp[0] = (DD)1; 240 | atd1* ptr = 0; 241 | new atd2[10]; 242 | f(b); 243 | int i = atd4::x; 244 | tds4(1, 2); 245 | inttd j; 246 | { 247 | typedef int Int; 248 | noopFunc([&](Int& i){}); 249 | } 250 | { 251 | f3(); 252 | } 253 | forwaredDeclared(); 254 | { 255 | WithDefaultTypeParam w; 256 | usedFunc1(); 257 | F f; 258 | G g; 259 | H::UsedAlias i; 260 | StructAlias sa; 261 | } 262 | } 263 | 264 | template 265 | void forwaredDeclared() { 266 | } 267 | 268 | -------------------------------------------------------------------------------- /tests/cases/inliner3/clangOptions.txt: -------------------------------------------------------------------------------- 1 | -std=c++11 2 | -------------------------------------------------------------------------------- /tests/cases/inliner3/etalon.cpp: -------------------------------------------------------------------------------- 1 | void f1() {} 2 | void f2() { 3 | f1(); 4 | } 5 | 6 | int gen() { 7 | static int c = 0; 8 | return ++c; 9 | } 10 | 11 | void f4() { 12 | struct S1 { 13 | }; 14 | S1 x; 15 | static int i = gen(); 16 | } 17 | 18 | 19 | template 20 | struct Outer 21 | { 22 | struct Inner 23 | { 24 | }; 25 | 26 | Inner inner; 27 | }; 28 | 29 | template 30 | void f3() { 31 | Outer v; 32 | (void)v.inner; 33 | } 34 | 35 | int f6() { 36 | return 6; 37 | } 38 | 39 | struct S2 { 40 | int i; 41 | S2() 42 | : i(f6()) 43 | {} 44 | }; 45 | 46 | int f7() { 47 | return 7; 48 | } 49 | 50 | void f8(int i = f7()) { 51 | } 52 | 53 | /// caide keep 54 | void f9() {} 55 | 56 | int i4 ; 57 | 58 | int i8, i10; 59 | 60 | #define td int 61 | 62 | td v2; 63 | 64 | void usedFunc() {} 65 | 66 | typedef double db; 67 | db dp[100]; 68 | 69 | struct A { 70 | static const int x = 2; 71 | }; 72 | typedef A VI; 73 | struct B : VI { 74 | }; 75 | 76 | typedef double DD; 77 | 78 | typedef A atd1; 79 | typedef A atd2; 80 | typedef A atd3; 81 | typedef A atd4; 82 | 83 | void f(atd3& a){} 84 | 85 | struct S4 { 86 | S4(int a, int b){} 87 | }; 88 | 89 | typedef S4 tds4; 90 | 91 | template 92 | struct Identity { typedef T type; }; 93 | 94 | typedef Identity::type inttd; 95 | 96 | template 97 | void noopFunc(T t) {} 98 | 99 | template 100 | void forwaredDeclared(); 101 | 102 | class DefaultTypeParam {}; 103 | 104 | template 105 | class WithDefaultTypeParam 106 | { 107 | }; 108 | 109 | template 110 | void usedFunc1(); 111 | 112 | template 113 | void usedFunc1() { 114 | } 115 | 116 | template 117 | struct E{}; 118 | 119 | template > 120 | struct F {}; 121 | 122 | struct G { 123 | void used() {} 124 | G() = default; 125 | G(const G&) = default; 126 | G& operator=(const G&) = delete; 127 | ~G() { 128 | used(); 129 | } 130 | }; 131 | 132 | template 133 | struct H { 134 | template 135 | using UsedAlias = int; 136 | }; 137 | 138 | template 139 | struct UsedThroughAlias { 140 | }; 141 | 142 | template 143 | using StructAlias = UsedThroughAlias; 144 | 145 | int main() { 146 | f2(); 147 | //f3(); 148 | f4(); 149 | S2 s2; 150 | f8(); 151 | i4 = i8 = i10 = 1; 152 | usedFunc(); 153 | v2 = 1; 154 | dp[0] = 1; 155 | B b; 156 | dp[0] = (DD)1; 157 | atd1* ptr = 0; 158 | new atd2[10]; 159 | f(b); 160 | int i = atd4::x; 161 | tds4(1, 2); 162 | inttd j; 163 | { 164 | typedef int Int; 165 | noopFunc([&](Int& i){}); 166 | } 167 | { 168 | f3(); 169 | } 170 | forwaredDeclared(); 171 | { 172 | WithDefaultTypeParam w; 173 | usedFunc1(); 174 | F f; 175 | G g; 176 | H::UsedAlias i; 177 | StructAlias sa; 178 | } 179 | } 180 | 181 | template 182 | void forwaredDeclared() { 183 | } 184 | 185 | -------------------------------------------------------------------------------- /tests/cases/line-directives/1.cpp: -------------------------------------------------------------------------------- 1 | #line 10 "foo.cpp" 2 | 3 | int main() { } 4 | -------------------------------------------------------------------------------- /tests/cases/line-directives/etalon.cpp: -------------------------------------------------------------------------------- 1 | int main() { } 2 | -------------------------------------------------------------------------------- /tests/cases/macros/1.cpp: -------------------------------------------------------------------------------- 1 | #define MACRO_TO_KEEP 2 | 3 | #define foo 1 4 | int a() { 5 | #if 123 < foo 6 | return 1; 7 | #else 8 | return 2; 9 | #endif 10 | } 11 | 12 | int b() { 13 | #if __cplusplus > 1 14 | return 1; 15 | #else 16 | return 2; 17 | #endif 18 | } 19 | 20 | #define bar 1 21 | int c() { 22 | #if 1 + 1 > 1*(bar+bar) 23 | return 1; 24 | #else 25 | return 2; 26 | #endif 27 | } 28 | 29 | int main() { 30 | return a() + b() + c(); 31 | } 32 | 33 | #define UNUSED 1 34 | int unused_func() { 35 | return UNUSED; 36 | } 37 | #undef UNUSED 38 | 39 | -------------------------------------------------------------------------------- /tests/cases/macros/etalon.cpp: -------------------------------------------------------------------------------- 1 | #define MACRO_TO_KEEP 2 | 3 | int a() { 4 | return 2; 5 | } 6 | 7 | int b() { 8 | return 1; 9 | } 10 | 11 | #define bar 1 12 | int c() { 13 | #if 1 + 1 > 1*(bar+bar) 14 | return 1; 15 | #else 16 | return 2; 17 | #endif 18 | } 19 | 20 | int main() { 21 | return a() + b() + c(); 22 | } 23 | -------------------------------------------------------------------------------- /tests/cases/macros/macrosToKeep.txt: -------------------------------------------------------------------------------- 1 | MACRO_TO_KEEP 2 | bar 3 | -------------------------------------------------------------------------------- /tests/cases/merge-namespaces-2/1.cpp: -------------------------------------------------------------------------------- 1 | namespace ns { 2 | void f(); 3 | } 4 | 5 | int main() { 6 | ns::f(); 7 | } 8 | 9 | namespace ns { 10 | void f() { } 11 | } 12 | 13 | -------------------------------------------------------------------------------- /tests/cases/merge-namespaces-2/etalon.cpp: -------------------------------------------------------------------------------- 1 | namespace ns { 2 | void f(); 3 | } 4 | 5 | int main() { 6 | ns::f(); 7 | } 8 | 9 | namespace ns { 10 | void f() { } 11 | } 12 | 13 | -------------------------------------------------------------------------------- /tests/cases/merge-namespaces/1.cpp: -------------------------------------------------------------------------------- 1 | namespace ns1 { 2 | void used1() {} 3 | } 4 | 5 | namespace ns1 { 6 | void used2() {} 7 | } 8 | 9 | namespace ns2 { 10 | namespace internal { 11 | void used1() {} 12 | } 13 | int unused = 0; 14 | void unused2(int a) {} 15 | } 16 | 17 | namespace ns2 { 18 | namespace internal { 19 | void used2() {} 20 | } 21 | int used = 1; 22 | } 23 | 24 | namespace ns2 { 25 | namespace internal { 26 | void used3() {} 27 | } 28 | } 29 | 30 | namespace ns3 { 31 | void used1() {} 32 | } 33 | 34 | struct Unused { 35 | Unused(const Unused& u) = delete; 36 | }; 37 | 38 | namespace ns3 { 39 | void used2() {} 40 | } 41 | 42 | int main() { 43 | ns1::used1(); 44 | ns1::used2(); 45 | ns2::internal::used1(); 46 | ns2::internal::used2(); 47 | ns2::internal::used3(); 48 | (void)ns2::used; 49 | ns3::used1(); 50 | ns3::used2(); 51 | } 52 | 53 | -------------------------------------------------------------------------------- /tests/cases/merge-namespaces/clangOptions.txt: -------------------------------------------------------------------------------- 1 | -std=c++11 2 | 3 | -------------------------------------------------------------------------------- /tests/cases/merge-namespaces/etalon.cpp: -------------------------------------------------------------------------------- 1 | namespace ns1 { 2 | void used1() {} 3 | void used2() {} 4 | } 5 | 6 | namespace ns2 { 7 | namespace internal { 8 | void used1() {} 9 | void used2() {} 10 | } 11 | int used = 1; 12 | namespace internal { 13 | void used3() {} 14 | } 15 | } 16 | 17 | namespace ns3 { 18 | void used1() {} 19 | void used2() {} 20 | } 21 | 22 | int main() { 23 | ns1::used1(); 24 | ns1::used2(); 25 | ns2::internal::used1(); 26 | ns2::internal::used2(); 27 | ns2::internal::used3(); 28 | (void)ns2::used; 29 | ns3::used1(); 30 | ns3::used2(); 31 | } 32 | 33 | -------------------------------------------------------------------------------- /tests/cases/pull-headers-up/1.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | void f(mystd::vector& v) {} 3 | 4 | #include 5 | int main() { 6 | mystd::map map; 7 | mystd::vector v; 8 | f(v); 9 | } 10 | -------------------------------------------------------------------------------- /tests/cases/pull-headers-up/clangOptions.txt: -------------------------------------------------------------------------------- 1 | -isystem 2 | TEST_ROOT/mystd 3 | -------------------------------------------------------------------------------- /tests/cases/pull-headers-up/etalon.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | void f(mystd::vector& v) {} 4 | 5 | int main() { 6 | mystd::map map; 7 | mystd::vector v; 8 | f(v); 9 | } 10 | -------------------------------------------------------------------------------- /tests/cases/pull-headers-up/mystd/mymap.h: -------------------------------------------------------------------------------- 1 | namespace mystd { 2 | class map { 3 | }; 4 | } 5 | -------------------------------------------------------------------------------- /tests/cases/pull-headers-up/mystd/myvector.h: -------------------------------------------------------------------------------- 1 | namespace mystd { 2 | class vector { 3 | }; 4 | } 5 | -------------------------------------------------------------------------------- /tests/cases/qualifiers/1.cpp: -------------------------------------------------------------------------------- 1 | struct A { 2 | void f() {} 3 | int i = 2; 4 | }; 5 | 6 | struct B: public A { 7 | using Base = A; 8 | void g() { 9 | Base::f(); 10 | } 11 | }; 12 | 13 | struct C: public A { 14 | using Base = A; 15 | void g() { 16 | (void)Base::i; 17 | } 18 | }; 19 | 20 | struct D { 21 | void f() {} 22 | }; 23 | struct E: private D { 24 | using Base = D; 25 | using Base::f; 26 | }; 27 | 28 | struct F { 29 | static int i; 30 | }; 31 | int F::i = 0; 32 | struct F1: private F { 33 | using F::i; 34 | }; 35 | 36 | int main() { 37 | B b; 38 | b.g(); 39 | C c; 40 | c.g(); 41 | E e; 42 | e.f(); 43 | int i = F1::i; 44 | (void)i; 45 | } 46 | 47 | -------------------------------------------------------------------------------- /tests/cases/qualifiers/clangOptions.txt: -------------------------------------------------------------------------------- 1 | -std=c++11 -------------------------------------------------------------------------------- /tests/cases/qualifiers/etalon.cpp: -------------------------------------------------------------------------------- 1 | struct A { 2 | void f() {} 3 | int i = 2; 4 | }; 5 | 6 | struct B: public A { 7 | using Base = A; 8 | void g() { 9 | Base::f(); 10 | } 11 | }; 12 | 13 | struct C: public A { 14 | using Base = A; 15 | void g() { 16 | (void)Base::i; 17 | } 18 | }; 19 | 20 | struct D { 21 | void f() {} 22 | }; 23 | struct E: private D { 24 | using Base = D; 25 | using Base::f; 26 | }; 27 | 28 | struct F { 29 | static int i; 30 | }; 31 | int F::i = 0; 32 | struct F1: private F { 33 | using F::i; 34 | }; 35 | 36 | int main() { 37 | B b; 38 | b.g(); 39 | C c; 40 | c.g(); 41 | E e; 42 | e.f(); 43 | int i = F1::i; 44 | (void)i; 45 | } 46 | 47 | -------------------------------------------------------------------------------- /tests/cases/references-from-template-arguments/1.cpp: -------------------------------------------------------------------------------- 1 | constexpr int a = 2; 2 | constexpr int b = 3; 3 | constexpr int c = 4; 4 | template void f() {} 5 | 6 | template<> void f() {} 7 | 8 | using A = int; 9 | template struct B {}; 10 | 11 | template struct C {}; 12 | 13 | template<> struct C {}; 14 | 15 | int main() { 16 | f(); 17 | f<6>(); 18 | B var; 19 | (void)var; 20 | C<8> var2; 21 | (void)var2; 22 | } 23 | 24 | -------------------------------------------------------------------------------- /tests/cases/references-from-template-arguments/clangOptions.txt: -------------------------------------------------------------------------------- 1 | -std=c++11 2 | 3 | -------------------------------------------------------------------------------- /tests/cases/references-from-template-arguments/etalon.cpp: -------------------------------------------------------------------------------- 1 | constexpr int a = 2; 2 | constexpr int b = 3; 3 | constexpr int c = 4; 4 | template void f() {} 5 | 6 | template<> void f() {} 7 | 8 | using A = int; 9 | template struct B {}; 10 | 11 | template struct C {}; 12 | 13 | template<> struct C {}; 14 | 15 | int main() { 16 | f(); 17 | f<6>(); 18 | B var; 19 | (void)var; 20 | C<8> var2; 21 | (void)var2; 22 | } 23 | 24 | -------------------------------------------------------------------------------- /tests/cases/remove-comments/1.cpp: -------------------------------------------------------------------------------- 1 | // Unused1 2 | // Multi-line 3 | // comment 4 | struct Unused1 {}; 5 | 6 | // Used 7 | struct Used1 { 8 | // unusedMethod: comment for declaration 9 | void unusedMethod(); 10 | 11 | // UnusedTypedef 12 | typedef int UnusedTypedef; 13 | }; 14 | 15 | /* Unused2 16 | Multi-line 17 | comment 18 | */ 19 | struct Unused2 {}; 20 | 21 | // unusedMethod: comment for definition 22 | void Used1::unusedMethod() { 23 | } 24 | 25 | int main() { 26 | Used1 u; 27 | return 0; 28 | } 29 | 30 | -------------------------------------------------------------------------------- /tests/cases/remove-comments/clangOptions.txt: -------------------------------------------------------------------------------- 1 | -fparse-all-comments 2 | -------------------------------------------------------------------------------- /tests/cases/remove-comments/etalon.cpp: -------------------------------------------------------------------------------- 1 | // Used 2 | struct Used1 { 3 | 4 | }; 5 | 6 | 7 | int main() { 8 | Used1 u; 9 | return 0; 10 | } 11 | 12 | 13 | -------------------------------------------------------------------------------- /tests/cases/remove-namespaces/1.cpp: -------------------------------------------------------------------------------- 1 | namespace ns1 { 2 | void unused() { } 3 | } 4 | 5 | namespace ns1 { 6 | void used() { } 7 | } 8 | 9 | namespace ns2 { 10 | void used() { } 11 | } 12 | 13 | namespace ns2 { 14 | void unused() {} 15 | } 16 | 17 | namespace outer1 { 18 | namespace inner1 { 19 | template 20 | struct void_t {}; 21 | } 22 | } 23 | 24 | namespace outer1 { 25 | namespace inner1 { 26 | template 27 | struct UsedClass { 28 | template 29 | void UnusedMethod(U u); 30 | }; 31 | } 32 | } 33 | 34 | namespace outer1 { 35 | namespace inner1 { 36 | template 37 | template 38 | void UsedClass::UnusedMethod(U u) {} 39 | } 40 | } 41 | 42 | namespace outer1 { 43 | static int unusedVariable = 1; 44 | } 45 | 46 | namespace outer1 { 47 | template 48 | using UnusedTypeAlias = T; 49 | } 50 | 51 | namespace outer2 { 52 | namespace inner1 { 53 | void used() {} 54 | } 55 | namespace inner2 { 56 | void unused() {} 57 | } 58 | } 59 | 60 | namespace outer3 { 61 | namespace inner1 { 62 | void unused() {} 63 | } 64 | } 65 | 66 | int main() { 67 | ns1::used(); 68 | ns2::used(); 69 | outer2::inner1::used(); 70 | { 71 | outer1::inner1::UsedClass x; 72 | } 73 | return 0; 74 | } 75 | 76 | -------------------------------------------------------------------------------- /tests/cases/remove-namespaces/clangOptions.txt: -------------------------------------------------------------------------------- 1 | -std=c++11 2 | -------------------------------------------------------------------------------- /tests/cases/remove-namespaces/etalon.cpp: -------------------------------------------------------------------------------- 1 | namespace ns1 { 2 | void used() { } 3 | } 4 | 5 | namespace ns2 { 6 | void used() { } 7 | } 8 | 9 | 10 | namespace outer1 { 11 | namespace inner1 { 12 | template 13 | struct UsedClass { 14 | }; 15 | } 16 | } 17 | 18 | namespace outer2 { 19 | namespace inner1 { 20 | void used() {} 21 | } 22 | } 23 | 24 | 25 | int main() { 26 | ns1::used(); 27 | ns2::used(); 28 | outer2::inner1::used(); 29 | { 30 | outer1::inner1::UsedClass x; 31 | } 32 | return 0; 33 | } 34 | 35 | 36 | -------------------------------------------------------------------------------- /tests/cases/remove-template-functions/1.cpp: -------------------------------------------------------------------------------- 1 | template 2 | void f1() {} 3 | 4 | template<> 5 | void f1() {} 6 | 7 | template 8 | void f2() {} 9 | 10 | template<> 11 | void f2() {} 12 | 13 | int main() { 14 | f1(); 15 | f2(); 16 | } 17 | 18 | -------------------------------------------------------------------------------- /tests/cases/remove-template-functions/etalon.cpp: -------------------------------------------------------------------------------- 1 | template 2 | void f1() {} 3 | 4 | template 5 | void f2() {} 6 | 7 | template<> 8 | void f2() {} 9 | 10 | int main() { 11 | f1(); 12 | f2(); 13 | } 14 | 15 | -------------------------------------------------------------------------------- /tests/cases/remove-type-alias/1.cpp: -------------------------------------------------------------------------------- 1 | using TypeAlias = int; 2 | 3 | template 4 | using AliasTemplate = T; 5 | 6 | int main() { 7 | } 8 | 9 | -------------------------------------------------------------------------------- /tests/cases/remove-type-alias/clangOptions.txt: -------------------------------------------------------------------------------- 1 | -std=c++11 -------------------------------------------------------------------------------- /tests/cases/remove-type-alias/etalon.cpp: -------------------------------------------------------------------------------- 1 | 2 | int main() { 3 | } 4 | 5 | -------------------------------------------------------------------------------- /tests/cases/sizeof/1.cpp: -------------------------------------------------------------------------------- 1 | int usedVar; 2 | struct usedType { }; 3 | int unusedVar; 4 | struct unusedType { }; 5 | 6 | int main() { 7 | int a = sizeof(usedVar), b = sizeof(usedType); 8 | (void)a; (void)b; 9 | } 10 | 11 | -------------------------------------------------------------------------------- /tests/cases/sizeof/etalon.cpp: -------------------------------------------------------------------------------- 1 | int usedVar; 2 | struct usedType { }; 3 | 4 | int main() { 5 | int a = sizeof(usedVar), b = sizeof(usedType); 6 | (void)a; (void)b; 7 | } 8 | 9 | -------------------------------------------------------------------------------- /tests/cases/source-ranges/1.cpp: -------------------------------------------------------------------------------- 1 | auto f() -> decltype(1); 2 | auto g() noexcept -> int; 3 | 4 | int g() noexcept { return 1; } 5 | 6 | int main() { } 7 | -------------------------------------------------------------------------------- /tests/cases/source-ranges/etalon.cpp: -------------------------------------------------------------------------------- 1 | int main() { } 2 | -------------------------------------------------------------------------------- /tests/cases/static-assert/1.cpp: -------------------------------------------------------------------------------- 1 | namespace case1 { 2 | using int2 = int; 3 | static_assert(int2{} == 0, ""); 4 | 5 | constexpr int c_zero = 0; 6 | static_assert(c_zero == 0, ""); 7 | 8 | template 9 | int func() { 10 | static_assert(n != 0, ""); 11 | return n; 12 | } 13 | 14 | void main() { 15 | int n = func<1>(); 16 | (void)n; 17 | } 18 | } 19 | 20 | int main() { 21 | case1::main(); 22 | } 23 | 24 | -------------------------------------------------------------------------------- /tests/cases/static-assert/clangOptions.txt: -------------------------------------------------------------------------------- 1 | -std=c++11 2 | -------------------------------------------------------------------------------- /tests/cases/static-assert/etalon.cpp: -------------------------------------------------------------------------------- 1 | namespace case1 { 2 | template 3 | int func() { 4 | return n; 5 | } 6 | 7 | void main() { 8 | int n = func<1>(); 9 | (void)n; 10 | } 11 | } 12 | 13 | int main() { 14 | case1::main(); 15 | } 16 | 17 | -------------------------------------------------------------------------------- /tests/cases/std-namespace/1.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace mystd; 3 | void f(vector& v) {} 4 | int main() { 5 | using namespace mystd; 6 | vector v; 7 | f(v); 8 | return 0; 9 | } 10 | -------------------------------------------------------------------------------- /tests/cases/std-namespace/clangOptions.txt: -------------------------------------------------------------------------------- 1 | -isystem 2 | TEST_ROOT/mystd 3 | -------------------------------------------------------------------------------- /tests/cases/std-namespace/etalon.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace mystd; 3 | void f(vector& v) {} 4 | int main() { 5 | vector v; 6 | f(v); 7 | return 0; 8 | } 9 | -------------------------------------------------------------------------------- /tests/cases/std-namespace/mystd/mystd.h: -------------------------------------------------------------------------------- 1 | namespace mystd { 2 | class vector { 3 | }; 4 | } 5 | -------------------------------------------------------------------------------- /tests/cases/stl/1.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | #include 57 | #include 58 | #include 59 | #include 60 | #include 61 | #include 62 | #include 63 | #include 64 | #include 65 | #include 66 | #include 67 | #include 68 | #include 69 | #include 70 | #include 71 | 72 | int main() { 73 | return 0; 74 | } 75 | -------------------------------------------------------------------------------- /tests/cases/stl/clangOptions.txt: -------------------------------------------------------------------------------- 1 | -v 2 | -------------------------------------------------------------------------------- /tests/cases/stl/etalon.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | #include 57 | #include 58 | #include 59 | #include 60 | #include 61 | #include 62 | #include 63 | #include 64 | #include 65 | #include 66 | #include 67 | #include 68 | #include 69 | #include 70 | #include 71 | 72 | int main() { 73 | return 0; 74 | } 75 | -------------------------------------------------------------------------------- /tests/cases/template-alias/1.cpp: -------------------------------------------------------------------------------- 1 | template 2 | struct Type { 3 | using type = int; 4 | }; 5 | 6 | template 7 | using Alias = typename Type::type; 8 | 9 | auto declval2() -> int*; 10 | 11 | template 12 | using Alias2 = decltype(*declval2()); 13 | 14 | using R2 = Alias2; 15 | 16 | void f2(R2 x) {} 17 | 18 | template 19 | auto declval3() -> T; 20 | 21 | template 22 | using Alias3 = decltype(*declval3()); 23 | 24 | using R3 = Alias3; 25 | 26 | void f3(R3 x) {} 27 | 28 | int main() { 29 | Alias a; 30 | (void)f2; 31 | (void)f3; 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /tests/cases/template-alias/clangOptions.txt: -------------------------------------------------------------------------------- 1 | -std=c++11 2 | -------------------------------------------------------------------------------- /tests/cases/template-alias/etalon.cpp: -------------------------------------------------------------------------------- 1 | template 2 | struct Type { 3 | using type = int; 4 | }; 5 | 6 | template 7 | using Alias = typename Type::type; 8 | 9 | auto declval2() -> int*; 10 | 11 | template 12 | using Alias2 = decltype(*declval2()); 13 | 14 | using R2 = Alias2; 15 | 16 | void f2(R2 x) {} 17 | 18 | template 19 | auto declval3() -> T; 20 | 21 | template 22 | using Alias3 = decltype(*declval3()); 23 | 24 | using R3 = Alias3; 25 | 26 | void f3(R3 x) {} 27 | 28 | int main() { 29 | Alias a; 30 | (void)f2; 31 | (void)f3; 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /tests/cases/template-friend/1.cpp: -------------------------------------------------------------------------------- 1 | template 2 | void funct(); 3 | 4 | void func(); 5 | 6 | template 7 | struct A; 8 | 9 | template 10 | struct B; 11 | 12 | template 13 | struct C; 14 | 15 | template 16 | class D { 17 | static void f(); 18 | 19 | friend struct A< B >; 20 | 21 | template 22 | friend struct C; 23 | 24 | template 25 | friend void funct(); 26 | 27 | friend void func(); 28 | }; 29 | 30 | template 31 | struct C { 32 | static void f() { 33 | D::f(); 34 | } 35 | }; 36 | 37 | template 38 | struct A {}; 39 | 40 | template<> 41 | struct A< B > { 42 | public: 43 | template 44 | static void f() { 45 | D::f(); 46 | } 47 | }; 48 | 49 | void func() { 50 | D::f(); 51 | } 52 | 53 | template 54 | void funct() { 55 | D::f(); 56 | } 57 | 58 | int main () { 59 | A< B >::f(); 60 | C::f(); 61 | funct(); 62 | func(); 63 | } 64 | -------------------------------------------------------------------------------- /tests/cases/template-friend/etalon.cpp: -------------------------------------------------------------------------------- 1 | template 2 | void funct(); 3 | 4 | void func(); 5 | 6 | template 7 | struct A; 8 | 9 | template 10 | struct B; 11 | 12 | template 13 | struct C; 14 | 15 | template 16 | class D { 17 | static void f(); 18 | 19 | friend struct A< B >; 20 | 21 | template 22 | friend struct C; 23 | 24 | template 25 | friend void funct(); 26 | 27 | friend void func(); 28 | }; 29 | 30 | template 31 | struct C { 32 | static void f() { 33 | D::f(); 34 | } 35 | }; 36 | 37 | template 38 | struct A {}; 39 | 40 | template<> 41 | struct A< B > { 42 | public: 43 | template 44 | static void f() { 45 | D::f(); 46 | } 47 | }; 48 | 49 | void func() { 50 | D::f(); 51 | } 52 | 53 | template 54 | void funct() { 55 | D::f(); 56 | } 57 | 58 | int main () { 59 | A< B >::f(); 60 | C::f(); 61 | funct(); 62 | func(); 63 | } 64 | -------------------------------------------------------------------------------- /tests/cases/template-variables/1.cpp: -------------------------------------------------------------------------------- 1 | template 2 | int I1 = 1; 3 | 4 | template 5 | int I2 = 2; 6 | 7 | struct S1 {}; 8 | 9 | template 10 | struct TS1 {}; 11 | 12 | template 13 | int I3 = 3; 14 | 15 | template 16 | int I3> = 31; 17 | 18 | template <> 19 | int I3> = 311; 20 | 21 | template <> 22 | int I3 = 32; 23 | 24 | int main() { 25 | (void)I2; 26 | (void)I3>; 27 | } 28 | -------------------------------------------------------------------------------- /tests/cases/template-variables/etalon.cpp: -------------------------------------------------------------------------------- 1 | template 2 | int I2 = 2; 3 | 4 | template 5 | struct TS1 {}; 6 | 7 | template 8 | int I3 = 3; 9 | 10 | template 11 | int I3> = 31; 12 | 13 | int main() { 14 | (void)I2; 15 | (void)I3>; 16 | } 17 | -------------------------------------------------------------------------------- /tests/cases/templated-context/1.cpp: -------------------------------------------------------------------------------- 1 | template 2 | struct Outer { 3 | using Type = T; 4 | 5 | template 6 | struct Inner { 7 | int a; 8 | 9 | template 10 | struct MoreInner { }; 11 | 12 | template 13 | struct MoreInnerUnused { }; 14 | }; 15 | }; 16 | 17 | template<> 18 | struct Outer { 19 | int another; 20 | }; 21 | 22 | int main() { 23 | Outer a; 24 | Outer::Type x; 25 | Outer::Inner c; 26 | (void)a; (void)x; 27 | (void)c.a; 28 | Outer::Inner::MoreInner i; 29 | (void)i; 30 | } 31 | 32 | -------------------------------------------------------------------------------- /tests/cases/templated-context/clangOptions.txt: -------------------------------------------------------------------------------- 1 | -std=c++11 2 | -------------------------------------------------------------------------------- /tests/cases/templated-context/etalon.cpp: -------------------------------------------------------------------------------- 1 | template 2 | struct Outer { 3 | using Type = T; 4 | 5 | template 6 | struct Inner { 7 | int a; 8 | 9 | template 10 | struct MoreInner { }; 11 | }; 12 | }; 13 | 14 | int main() { 15 | Outer a; 16 | Outer::Type x; 17 | Outer::Inner c; 18 | (void)a; (void)x; 19 | (void)c.a; 20 | Outer::Inner::MoreInner i; 21 | (void)i; 22 | } 23 | 24 | -------------------------------------------------------------------------------- /tests/cases/track-parent-decls/1.cpp: -------------------------------------------------------------------------------- 1 | template 2 | struct F { 3 | template 4 | F(const T& f) {} 5 | }; 6 | 7 | void used() {} 8 | 9 | int main() { 10 | F f = [&](int i) { 11 | used(); 12 | }; 13 | return 0; 14 | } 15 | 16 | -------------------------------------------------------------------------------- /tests/cases/track-parent-decls/clangOptions.txt: -------------------------------------------------------------------------------- 1 | -std=c++11 2 | 3 | -------------------------------------------------------------------------------- /tests/cases/track-parent-decls/etalon.cpp: -------------------------------------------------------------------------------- 1 | template 2 | struct F { 3 | template 4 | F(const T& f) {} 5 | }; 6 | 7 | void used() {} 8 | 9 | int main() { 10 | F f = [&](int i) { 11 | used(); 12 | }; 13 | return 0; 14 | } 15 | 16 | -------------------------------------------------------------------------------- /tests/cases/ull/1.cpp: -------------------------------------------------------------------------------- 1 | using ull = unsigned long long; 2 | 3 | int main() { 4 | ull i = 0; 5 | } 6 | -------------------------------------------------------------------------------- /tests/cases/ull/etalon.cpp: -------------------------------------------------------------------------------- 1 | using ull = unsigned long long; 2 | 3 | int main() { 4 | ull i = 0; 5 | } 6 | -------------------------------------------------------------------------------- /tests/cases/unused-fields/1.cpp: -------------------------------------------------------------------------------- 1 | struct A { 2 | int a, b, c, d; 3 | bool e, f, g; 4 | static long h, i; 5 | }; 6 | 7 | long A::h = 1, A::i = 2; 8 | 9 | int main() { 10 | A x; 11 | x.b = x.c = A::h; 12 | x.g = false; 13 | } 14 | -------------------------------------------------------------------------------- /tests/cases/unused-fields/etalon.cpp: -------------------------------------------------------------------------------- 1 | struct A { 2 | int b, c; 3 | bool g; 4 | static long h; 5 | }; 6 | 7 | long A::h = 1; 8 | 9 | int main() { 10 | A x; 11 | x.b = x.c = A::h; 12 | x.g = false; 13 | } 14 | -------------------------------------------------------------------------------- /tests/cases/using-declarations/1.cpp: -------------------------------------------------------------------------------- 1 | namespace ns1 { 2 | void unused() {} 3 | } 4 | 5 | namespace ns2 { 6 | void used() {} 7 | } 8 | 9 | using namespace ns1; 10 | using namespace ns2; 11 | 12 | namespace ns2 { 13 | void unused(); 14 | } 15 | 16 | using namespace ns2; 17 | 18 | namespace ns3 { 19 | void used3(); 20 | } 21 | 22 | void f1() { 23 | using namespace ns3; 24 | used3(); 25 | } 26 | 27 | namespace ns4 { 28 | namespace level2 { 29 | using namespace ns3; 30 | namespace level3 { 31 | using namespace ns3; 32 | void used() { 33 | used3(); 34 | } 35 | } 36 | } 37 | } 38 | 39 | namespace ns4 { 40 | using namespace ns3; 41 | namespace level2 { 42 | using namespace ns3; 43 | } 44 | } 45 | 46 | using namespace ns3; 47 | 48 | int main() { 49 | used(); 50 | f1(); 51 | ns4::level2::level3::used(); 52 | } 53 | 54 | -------------------------------------------------------------------------------- /tests/cases/using-declarations/etalon.cpp: -------------------------------------------------------------------------------- 1 | 2 | namespace ns2 { 3 | void used() {} 4 | } 5 | 6 | using namespace ns2; 7 | 8 | namespace ns3 { 9 | void used3(); 10 | } 11 | 12 | void f1() { 13 | using namespace ns3; 14 | used3(); 15 | } 16 | 17 | namespace ns4 { 18 | namespace level2 { 19 | using namespace ns3; 20 | namespace level3 { 21 | void used() { 22 | used3(); 23 | } 24 | } 25 | } 26 | using namespace ns3; 27 | } 28 | 29 | using namespace ns3; 30 | 31 | int main() { 32 | used(); 33 | f1(); 34 | ns4::level2::level3::used(); 35 | } 36 | 37 | -------------------------------------------------------------------------------- /tests/temp/keepme.txt: -------------------------------------------------------------------------------- 1 | This is a temporary folder where test runner dumps intermediate files 2 | 3 | -------------------------------------------------------------------------------- /tools/cfapi.py: -------------------------------------------------------------------------------- 1 | import httplib 2 | import urllib 3 | import time 4 | import random 5 | import hashlib 6 | import json 7 | import os.path 8 | import os 9 | from HTMLParser import HTMLParser 10 | from htmlentitydefs import name2codepoint 11 | from collections import defaultdict 12 | 13 | from cfkeys import KEY, SECRET 14 | 15 | 16 | def api_request(method_name, params): 17 | now = int(time.time()) 18 | params = [(k, str(v)) for (k, v) in params] 19 | params += [('apiKey', KEY), ('time', now), ('lang', 'en')] 20 | params = sorted(params) 21 | url = method_name + '?' + urllib.urlencode(params) 22 | rand_prefix = str(random.randint(100000, 999999)) 23 | digest = hashlib.sha512(rand_prefix + '/' + url + '#' + SECRET).hexdigest() 24 | params.append(('apiSig', rand_prefix + digest)) 25 | 26 | conn = httplib.HTTPConnection('codeforces.com') 27 | url = 'http://codeforces.com/api/' + method_name + '?' + urllib.urlencode(params) 28 | conn.request('GET', url) 29 | response = conn.getresponse() 30 | print response.status, response.reason 31 | data = response.read() 32 | conn.close() 33 | return json.loads(data) 34 | 35 | 36 | def get_user_info(users): 37 | params = [('handles', ';'.join(users))] 38 | return api_request('user.info', params) 39 | 40 | 41 | def get_contest_status(contest_id): 42 | params = [('contestId', contest_id)] 43 | return api_request('contest.status', params) 44 | 45 | 46 | def download_submission_from_url(url): 47 | conn = httplib.HTTPConnection('codeforces.com') 48 | conn.request('GET', url) 49 | response = conn.getresponse() 50 | print response.status, response.reason 51 | html_page = response.read() 52 | conn.close() 53 | 54 | class ProblemParser(HTMLParser): 55 | def __init__(self): 56 | HTMLParser.__init__(self) 57 | self.is_problem_text = False 58 | self.problem_text = '' 59 | 60 | def handle_starttag(self, tag, attrs): 61 | if tag == 'pre' and not self.is_problem_text: 62 | for (k, v) in attrs: 63 | if k == 'class' and v.find('program-source') >= 0: 64 | self.is_problem_text = True 65 | 66 | def handle_endtag(self, tag): 67 | if tag == 'pre': 68 | self.is_problem_text = False 69 | 70 | def handle_data(self, data): 71 | if self.is_problem_text: 72 | self.problem_text += data 73 | 74 | def handle_entityref(self, name): 75 | if self.is_problem_text: 76 | self.problem_text += chr(name2codepoint[name]) 77 | 78 | def handle_charref(self, name): 79 | if self.is_problem_text: 80 | if name.startswith('x'): 81 | self.problem_text += chr(int(name[1:], 16)) 82 | else: 83 | self.problem_text += chr(int(name)) 84 | 85 | parser = ProblemParser() 86 | parser.feed(html_page) 87 | parser.close() 88 | return parser.problem_text 89 | 90 | 91 | def download_submission(contest_id, submission_id): 92 | url = 'http://codeforces.com/contest/' + str(contest_id) + '/submission/' + str(submission_id) 93 | return download_submission_from_url(url) 94 | 95 | 96 | def download_submissions(contest_id): 97 | contest_dir = 'cf' + str(contest_id) 98 | try: 99 | os.makedirs(contest_dir) 100 | except: 101 | pass 102 | 103 | print 'Getting submission list for contest id=' + str(contest_id) 104 | j = get_contest_status(contest_id) 105 | 106 | print 'Grouping...' 107 | grouped = defaultdict(list) # group by author and problem 108 | for submission in j['result']: 109 | if submission['programmingLanguage'].lower().find('c++') < 0: 110 | continue 111 | if 'verdict' not in submission: 112 | continue 113 | if submission['verdict'] in ['COMPILATION_ERROR', 'SECURITY_VIOLATED', 'SKIPPED', 114 | 'REJECTED']: 115 | continue 116 | 117 | party = submission['author'] 118 | if 'teamId' in party: 119 | author_id = party['teamId'] 120 | else: 121 | author_id = party['members'][0]['handle'] 122 | problem_id = submission['problem']['index'] 123 | grouped[(author_id, problem_id)].append(submission) 124 | 125 | print 'Downloading...' 126 | for submissions in grouped.viewvalues(): 127 | submissions.sort(key=lambda s: s['creationTimeSeconds']) 128 | latest = submissions[-1:] 129 | for submission in latest: 130 | submission_id = submission['id'] 131 | filename = os.path.join(contest_dir, str(submission_id) + '.cpp') 132 | if not os.path.isfile(filename): 133 | print submission_id 134 | code = download_submission(contest_id, submission_id) 135 | with open(filename, 'w') as f: 136 | f.write(code) 137 | time.sleep(1.0) # 1 second 138 | 139 | 140 | def test(): 141 | #print download_submission(534, 10976539) 142 | print download_submissions(547) 143 | 144 | 145 | if __name__ == "__main__": 146 | test() 147 | 148 | --------------------------------------------------------------------------------