├── .codecov.yml ├── .dir-locals.el ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── Gemfile ├── LICENSE ├── README.md ├── TODO.txt ├── at.sh ├── bash └── funcs.bash ├── ci.sh ├── dpp2 ├── source │ └── dpp2 │ │ ├── expansion │ │ └── package.d │ │ ├── sea │ │ ├── node.d │ │ ├── package.d │ │ └── type.d │ │ ├── transform │ │ └── package.d │ │ └── translation │ │ ├── node.d │ │ └── type │ │ └── package.d └── tests │ └── ut │ ├── package.d │ ├── transform │ ├── clang.d │ ├── cursor.d │ ├── package.d │ └── type.d │ └── translation │ ├── node │ ├── package.d │ └── structs.d │ └── type │ ├── array.d │ ├── package.d │ ├── pointer.d │ └── primitives.d ├── dub.sdl ├── dub.selections.json ├── examples ├── .gitignore ├── all.sh ├── compile │ ├── .gitignore │ └── all.sh ├── download │ ├── .gitignore │ ├── all.sh │ ├── anatomy.dpp │ ├── etpan.dpp │ ├── imap.dpp │ ├── nanomsg.dpp │ ├── virt.dpp │ └── zfs.dpp └── run │ ├── all.sh │ ├── curl.dpp │ ├── openssl.dpp │ ├── pthread.dpp │ ├── run.sh │ └── stdlib.dpp ├── features ├── defines.feature ├── multiple_files.feature ├── preprocess.feature ├── simple_c_header.feature ├── simple_cpp_header.feature └── support │ └── env.rb ├── reggaefile.d ├── source ├── dpp │ ├── clang │ │ └── package.d │ ├── expansion │ │ └── package.d │ ├── from.d │ ├── runtime │ │ ├── app.d │ │ ├── context.d │ │ ├── options.d │ │ └── package.d │ └── translation │ │ ├── aggregate.d │ │ ├── dlang.d │ │ ├── docs.d │ │ ├── enum_.d │ │ ├── exception.d │ │ ├── function_.d │ │ ├── macro_.d │ │ ├── namespace.d │ │ ├── package.d │ │ ├── template_.d │ │ ├── tokens.d │ │ ├── translation.d │ │ ├── type │ │ └── package.d │ │ ├── typedef_.d │ │ └── variable.d └── main.d └── tests ├── common └── package.d ├── contract ├── aggregates.d ├── array.d ├── constexpr.d ├── enums.d ├── functions.d ├── inheritance.d ├── issues.d ├── macro_.d ├── main.d ├── member.d ├── methods.d ├── namespace.d ├── operators.d ├── package.d ├── templates.d └── typedef_.d ├── it ├── c │ ├── compile │ │ ├── array.d │ │ ├── collision.d │ │ ├── enum_.d │ │ ├── extensions.d │ │ ├── function_.d │ │ ├── preprocessor.d │ │ ├── projects.d │ │ ├── runtime_args.d │ │ ├── struct_.d │ │ ├── typedef_.d │ │ └── union_.d │ ├── dstep │ │ ├── functional.d │ │ ├── issues.d │ │ └── ut.d │ └── run │ │ ├── c.d │ │ ├── package.d │ │ └── struct_.d ├── cpp │ ├── class_.d │ ├── function_.d │ ├── misc.d │ ├── opaque.d │ ├── run.d │ └── templates.d ├── docs.d ├── expansion.d ├── issues.d ├── main.d └── package.d ├── test_main.d └── ut ├── expansion.d ├── old └── type.d └── package.d /.codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | notify: 3 | after_n_builds: 1 4 | 5 | coverage: 6 | precision: 3 7 | round: down 8 | range: 80...100 9 | 10 | status: 11 | project: true 12 | patch: true 13 | changes: true 14 | 15 | comment: false 16 | 17 | ignore: 18 | - "tests/*" 19 | -------------------------------------------------------------------------------- /.dir-locals.el: -------------------------------------------------------------------------------- 1 | ;;; Directory Local Variables 2 | ;;; See Info node `(emacs) Directory Variables' for more information. 3 | 4 | ((d-mode 5 | (fldd-dub-configuration . "unittest"))) 6 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | test: 6 | if: "!contains(github.event.head_commit.message, '[skip ci]')" 7 | name: Dub Test 8 | strategy: 9 | fail-fast: false 10 | matrix: 11 | os: 12 | - ubuntu-22.04 13 | - ubuntu-20.04 14 | - windows-2022 15 | #- macos-10.15 16 | dc: 17 | - dmd-2.108.1 18 | - dmd-2.102.1 19 | - ldc-1.38.0 20 | - ldc-1.31.0 21 | arch: 22 | - x86_64 23 | clang: 24 | - 12.0.0 25 | - 17.0.6 26 | runs-on: ${{ matrix.os }} 27 | steps: 28 | - uses: actions/checkout@v4 29 | 30 | - uses: ruby/setup-ruby@v1 31 | with: 32 | ruby-version: 3 33 | bundler-cache: true 34 | # Ruby puts this on the path which breaks ldc 35 | - name: Rename msys64/link.exe 36 | if: startsWith(matrix.os,'windows') 37 | shell: pwsh 38 | run: mv C:\msys64\usr\bin\link.exe C:\msys64\usr\bin\msys-link.exe 39 | 40 | - name: Cache 41 | id: cache 42 | uses: actions/cache@v4 43 | with: 44 | path: | 45 | ~/llvm 46 | key: cache-os:${{ matrix.os }}-clang:${{ matrix.clang }} 47 | 48 | - name: Setup D 49 | uses: dlang-community/setup-dlang@v1.4.0 50 | with: 51 | compiler: ${{ matrix.dc }} 52 | 53 | - name: Setup C++ 54 | uses: aminya/setup-cpp@v1 55 | with: 56 | llvm: ${{ matrix.clang }} 57 | 58 | - name: Run tests 59 | continue-on-error: ${{ contains(matrix.os, 'macos') }} 60 | run: dub test -q --build=unittest-cov --arch=${{ matrix.arch }} 61 | env: 62 | LIBRARY_PATH: "${{ env.LLVM_PATH }}/lib" 63 | 64 | - name: Build binary 65 | run: dub build -q --arch=${{ matrix.arch }} 66 | env: 67 | LIBRARY_PATH: "${{ env.LLVM_PATH }}/lib" 68 | 69 | - uses: codecov/codecov-action@v4 70 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .dub 2 | docs.json 3 | __dummy.html 4 | *.o 5 | *.obj 6 | __test__*__ 7 | bin 8 | tmp 9 | .reggae 10 | .ninja_* 11 | *.ninja 12 | compile_commands.json 13 | dub_describe.json 14 | *.lst -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'aruba', '1.1.1' 4 | gem 'cucumber', '6.1.0' 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /TODO.txt: -------------------------------------------------------------------------------- 1 | Misc 2 | * volatile member functions 3 | * Definitions in headers (no code to link to, always inlined) 4 | - See clang_Cursor_isFunctionInlined 5 | 6 | Classes 7 | * Use clang_getTemplateCursorKind to pick between `class` or `struct` 8 | 9 | Operators 10 | * User-defined literals 11 | 12 | Namespaces 13 | * Collate all namespaces in order to declare things correctly (nested namespaces) 14 | * extern "C" (LinkageSpec in clang - extern(C) inside extern(C++)?) 15 | 16 | Inheritance 17 | * Translate CXXBaseSpecifier 18 | * Use heuristic to pick `struct`, `class`, or `interface` 19 | * What to do with multiple inheritance? 20 | 21 | Templates 22 | * Default template parameters 23 | * Methods of template classes (no mangling since not instantiated) 24 | * Template template parameters 25 | * Function templates 26 | * Template specialisation 27 | * Class template partial specialisation 28 | -------------------------------------------------------------------------------- /at.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Run all acceptance tests 4 | 5 | set -euo pipefail 6 | 7 | SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 8 | BIN_DIR="$SCRIPT_DIR"/bin 9 | 10 | # shellcheck source=bash/funcs.bash 11 | source "$SCRIPT_DIR/bash/funcs.bash" 12 | 13 | clear 14 | build_dpp 15 | bundle exec cucumber --tags ~@wip 16 | ./examples/all.sh 17 | -------------------------------------------------------------------------------- /bash/funcs.bash: -------------------------------------------------------------------------------- 1 | # just to shut it up 2 | pushd () { 3 | command pushd "$@" > /dev/null 4 | } 5 | 6 | # just to shut it up 7 | popd () { 8 | command popd > /dev/null 9 | } 10 | 11 | git_clone() { 12 | git clone --recursive "$1" --depth=1 --branch master 13 | } 14 | 15 | mk() { 16 | make -j"$(nproc)" 17 | } 18 | 19 | # Build an up-to-date binary 20 | build_dpp () { 21 | if [[ -e "$BIN_DIR"/build.ninja ]]; then 22 | ninja -C "$BIN_DIR" d++ > /dev/null 23 | else 24 | pushd "$BIN_DIR/.." || exit 1 25 | dub build 26 | popd || exit 1 27 | fi 28 | } 29 | -------------------------------------------------------------------------------- /ci.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | DC="${DC:-dmd}" 6 | 7 | dub test -q --build=unittest-cov --compiler="$DC" 8 | # dub run -q -c dpp2 --build=unittest --compiler="$DC" 9 | dub build -q --compiler="$DC" 10 | 11 | if [[ "$DC" == "dmd" ]]; then 12 | bundle exec cucumber --tags ~@wip 13 | fi 14 | -------------------------------------------------------------------------------- /dpp2/source/dpp2/expansion/package.d: -------------------------------------------------------------------------------- 1 | /** 2 | Deals with expanding #include directives inline. 3 | */ 4 | module dpp2.expansion; 5 | 6 | 7 | import dpp.from; 8 | 9 | 10 | /** 11 | Params: 12 | translUnitFileName = The file name with all #include directives to parse 13 | context = The translation context 14 | language = Whether it's a C or C++ file 15 | includePaths = The list of files to pass as -I options to clang 16 | */ 17 | void expand(in string translUnitFileName, 18 | ref from!"dpp.runtime.context".Context context, 19 | in string[] includePaths, 20 | in string file = __FILE__, 21 | in size_t line = __LINE__) 22 | @safe 23 | { 24 | import dpp.runtime.context: Language; 25 | import dpp2.translation.node: translate; 26 | import dpp2.transform: toNode; 27 | import clang: parse; 28 | 29 | const extern_ = context.language == Language.Cpp ? "extern(C++)" : "extern(C)"; 30 | context.writeln([extern_, "{"]); 31 | 32 | const translationUnit = parseTU(translUnitFileName, context); 33 | auto nodes = translationUnit.cursor.toNode; 34 | 35 | foreach(node; nodes) { 36 | const indentation = context.indentation; 37 | const lines = translate(node); 38 | if(lines.length) context.writeln(lines); 39 | context.setIndentation(indentation); 40 | } 41 | 42 | context.writeln(["}", ""]); 43 | context.writeln(""); 44 | } 45 | 46 | 47 | // returns a range of dpp2.sea.Node 48 | private auto cursorsToNodes(in from!"clang".Cursor[] cursors) @safe { 49 | import dpp2.transform: toNode; 50 | import clang: Cursor; 51 | import std.algorithm: map, filter; 52 | import std.array: join; 53 | 54 | auto nested = cursors 55 | .filter!(c => c.kind != Cursor.Kind.MacroDefinition && c.kind != Cursor.Kind.InclusionDirective) 56 | .map!toNode 57 | ; 58 | return () @trusted { return nested.join; }(); 59 | } 60 | 61 | private from!"clang".TranslationUnit parseTU( 62 | in string translUnitFileName, 63 | ref from!"dpp.runtime.context".Context context, 64 | ) 65 | @safe 66 | { 67 | import clang: parse, TranslationUnitFlags; 68 | const args = clangArgs(context, translUnitFileName); 69 | return parse(translUnitFileName, 70 | args, 71 | TranslationUnitFlags.DetailedPreprocessingRecord); 72 | } 73 | 74 | string[] clangArgs(in from!"dpp.runtime.context".Context context, 75 | in string inputFileName) 76 | @safe pure 77 | { 78 | import dpp.runtime.context: Language; 79 | import std.algorithm: map; 80 | import std.range: chain; 81 | import std.array: array; 82 | 83 | auto args = 84 | chain( 85 | includePaths(context.options, inputFileName).map!(a => "-I" ~ a), 86 | context.options.defines.map!(a => "-D" ~ a) 87 | ).array; 88 | 89 | if(context.options.parseAsCpp || context.language == Language.Cpp) 90 | args ~= ["-xc++", "-std=c++14"]; 91 | else 92 | args ~= "-xc"; 93 | 94 | return args; 95 | } 96 | 97 | 98 | string[] includePaths(in from!"dpp.runtime.options".Options options, 99 | in string inputFileName) 100 | @safe pure 101 | { 102 | import std.path: dirName; 103 | return options.includePaths.dup ~ inputFileName.dirName; 104 | } 105 | 106 | 107 | bool isCppHeader(in from!"dpp.runtime.options".Options options, in string headerFileName) @safe pure { 108 | import std.path: extension; 109 | if(options.parseAsCpp) return true; 110 | return headerFileName.extension != ".h"; 111 | } 112 | 113 | 114 | string getHeaderName(in const(char)[] line, in string[] includePaths) 115 | @safe 116 | { 117 | const name = getHeaderName(line); 118 | return name == "" ? name : fullPath(includePaths, name); 119 | } 120 | 121 | 122 | string getHeaderName(const(char)[] line) 123 | @safe pure 124 | { 125 | import std.algorithm: startsWith, countUntil; 126 | import std.range: dropBack; 127 | import std.array: popFront; 128 | import std.string: stripLeft; 129 | 130 | line = line.stripLeft; 131 | if(!line.startsWith(`#include `)) return ""; 132 | 133 | const openingQuote = line.countUntil!(a => a == '"' || a == '<'); 134 | const closingQuote = line[openingQuote + 1 .. $].countUntil!(a => a == '"' || a == '>') + openingQuote + 1; 135 | return line[openingQuote + 1 .. closingQuote].idup; 136 | } 137 | 138 | 139 | // transforms a header name, e.g. stdio.h 140 | // into a full file path, e.g. /usr/include/stdio.h 141 | private string fullPath(in string[] includePaths, in string headerName) @safe { 142 | 143 | import std.algorithm: map, filter; 144 | import std.path: buildPath, absolutePath; 145 | import std.file: exists; 146 | import std.conv: text; 147 | import std.exception: enforce; 148 | 149 | if(headerName.exists) return headerName; 150 | 151 | auto filePaths = includePaths 152 | .map!(a => buildPath(a, headerName).absolutePath) 153 | .filter!exists; 154 | 155 | enforce(!filePaths.empty, text("d++ cannot find file path for header '", headerName, "'")); 156 | 157 | return filePaths.front; 158 | } 159 | -------------------------------------------------------------------------------- /dpp2/source/dpp2/sea/node.d: -------------------------------------------------------------------------------- 1 | /** 2 | Pertaining to nodes in the C/C++ AST. 3 | */ 4 | module dpp2.sea.node; 5 | 6 | 7 | import dpp.from; 8 | 9 | 10 | alias Node = from!"sumtype".SumType!( 11 | Struct, 12 | Field, 13 | Typedef, 14 | ); 15 | 16 | 17 | struct Struct { 18 | string spelling; 19 | Node[] nodes; 20 | // Anonymous structs still have a type, and that type has a name 21 | string typeSpelling; 22 | } 23 | 24 | 25 | struct Field { 26 | import dpp2.sea.type: Type; 27 | Type type; 28 | string spelling; 29 | } 30 | 31 | 32 | struct Typedef { 33 | import dpp2.sea.type: Type; 34 | string spelling; 35 | Type underlying; 36 | } 37 | -------------------------------------------------------------------------------- /dpp2/source/dpp2/sea/package.d: -------------------------------------------------------------------------------- 1 | /** 2 | Lacking a better way to colletively refer to C and C++, this package does 3 | it under the name "sea". 4 | */ 5 | module dpp2.sea; 6 | -------------------------------------------------------------------------------- /dpp2/source/dpp2/sea/type.d: -------------------------------------------------------------------------------- 1 | module dpp2.sea.type; 2 | 3 | 4 | import dpp.from; 5 | 6 | 7 | alias Type = from!"sumtype".SumType!( 8 | Void, NullPointerT, 9 | Bool, 10 | UnsignedChar, SignedChar, Char, Wchar, Char16, Char32, 11 | Short, UnsignedShort, Int, UnsignedInt, Long, UnsignedLong, LongLong, UnsignedLongLong, Int128, UnsignedInt128, 12 | Half, Float, Double, LongDouble, 13 | Pointer, 14 | ConstantArray, 15 | UserDefinedType, 16 | ); 17 | 18 | 19 | struct Void {} 20 | struct NullPointerT {} 21 | 22 | struct Bool {} 23 | 24 | struct UnsignedChar {} 25 | struct SignedChar {} 26 | struct Char {} 27 | struct Wchar {} 28 | struct Char16 {} 29 | struct Char32 {} 30 | 31 | struct Short {} 32 | struct UnsignedShort {} 33 | struct Int {} 34 | struct UnsignedInt {} 35 | struct Long {} 36 | struct UnsignedLong {} 37 | struct LongLong {} 38 | struct UnsignedLongLong {} 39 | struct Int128 {} 40 | struct UnsignedInt128 {} 41 | 42 | struct Half {} 43 | struct Float {} 44 | struct Double {} 45 | struct LongDouble {} 46 | 47 | 48 | struct Pointer { 49 | // has to be a pointer in order to have a recursive type 50 | Type* pointeeType; 51 | } 52 | 53 | 54 | struct ConstantArray { 55 | // has to be a pointer in order to have a recursive type 56 | Type* elementType; 57 | int length; 58 | } 59 | 60 | struct UserDefinedType { 61 | string spelling; 62 | } 63 | -------------------------------------------------------------------------------- /dpp2/source/dpp2/transform/package.d: -------------------------------------------------------------------------------- 1 | /** 2 | From clang's cursors to dpp's nodes. 3 | */ 4 | module dpp2.transform; 5 | 6 | 7 | import dpp.from; 8 | 9 | 10 | /** 11 | Transforms a clang `Cursor` (or a mock) into a dpp `Node` 12 | */ 13 | from!"dpp2.sea.node".Node[] toNode(C)(in C cursor) @trusted { 14 | 15 | import std.traits: isPointer; 16 | import std.conv: text; 17 | 18 | static if(isPointer!C) 19 | const cursorText = text(*cursor, " def? ", cursor.isDefinition); 20 | else 21 | const cursorText = text( cursor, " def? ", cursor.isDefinition); 22 | 23 | version(unittest) { 24 | import unit_threaded; 25 | () @trusted { writelnUt("toNode cursor: ", cursorText); }(); 26 | } 27 | 28 | auto transform = cursor.kind in transformations!C; 29 | 30 | if(transform is null) 31 | throw new Exception("Unknown cursor kind: " ~ cursorText); 32 | 33 | return (*transform)(cursor); 34 | } 35 | 36 | auto transformations(C)() { 37 | import clang: Cursor; 38 | with(Cursor.Kind) { 39 | return [ 40 | TranslationUnit: &fromTranslationUnit!C, 41 | StructDecl: &fromStruct!C, 42 | FieldDecl: &fromField!C, 43 | TypedefDecl: &fromTypedef!C, 44 | ]; 45 | } 46 | } 47 | 48 | from!"dpp2.sea.node".Node[] fromTranslationUnit(C)(in C cursor) @trusted 49 | in(cursor.kind == from!"clang".Cursor.Kind.TranslationUnit) 50 | do 51 | { 52 | import std.algorithm: filter, map; 53 | import std.array: join; 54 | 55 | auto cursors = cursor.children 56 | .filter!(a => a.isDefinition) 57 | ; 58 | 59 | return cursors.map!(c => toNode(c)).join; 60 | } 61 | 62 | 63 | from!"dpp2.sea.node".Node[] fromStruct(C)(in C cursor) @trusted 64 | in(cursor.kind == from!"clang".Cursor.Kind.StructDecl) 65 | do 66 | { 67 | import dpp2.sea.node: Node, Struct; 68 | import std.algorithm: map; 69 | import std.array: join; 70 | 71 | return [ 72 | Node( 73 | Struct(cursor.spelling, 74 | cursor.children.map!(c => toNode(c)).join, 75 | cursor.type.spelling), 76 | ) 77 | ]; 78 | } 79 | 80 | 81 | 82 | from!"dpp2.sea.node".Node[] fromField(C)(in C cursor) @trusted 83 | in(cursor.kind == from!"clang".Cursor.Kind.FieldDecl) 84 | do 85 | { 86 | import dpp2.sea.node: Node, Field; 87 | return [Node(Field(toType(cursor.type), cursor.spelling))]; 88 | } 89 | 90 | 91 | from!"dpp2.sea.node".Node[] fromTypedef(C)(in C cursor) @trusted 92 | in(cursor.kind == from!"clang".Cursor.Kind.TypedefDecl) 93 | do 94 | { 95 | import dpp2.sea.node: Node, Typedef; 96 | return [Node(Typedef(cursor.spelling, cursor.underlyingType.toType))]; 97 | } 98 | 99 | 100 | from!"dpp2.sea.type".Type toType(T)(in T clangType) @safe { 101 | import dpp2.sea.type; 102 | import std.conv: text; 103 | 104 | alias Kind = clangType.Kind; 105 | 106 | switch(clangType.kind) { 107 | 108 | default: 109 | throw new Exception(text("Unknown type kind: ", clangType)); 110 | 111 | case Kind.Void: return Type(Void()); 112 | case Kind.NullPtr: return Type(NullPointerT()); 113 | case Kind.Bool: return Type(Bool()); 114 | 115 | case Kind.WChar: return Type(Wchar()); 116 | case Kind.SChar: return Type(SignedChar()); 117 | case Kind.Char16: return Type(Char16()); 118 | case Kind.Char32: return Type(Char32()); 119 | case Kind.UChar: return Type(UnsignedChar()); 120 | case Kind.Char_U: return Type(UnsignedChar()); 121 | case Kind.Char_S: return Type(SignedChar()); 122 | 123 | case Kind.UShort: return Type(UnsignedShort()); 124 | case Kind.Short: return Type(Short()); 125 | case Kind.Int: return Type(Int()); 126 | case Kind.UInt: return Type(UnsignedInt()); 127 | case Kind.Long: return Type(Long()); 128 | case Kind.ULong: return Type(UnsignedLong()); 129 | case Kind.LongLong: return Type(LongLong()); 130 | case Kind.ULongLong: return Type(UnsignedLongLong()); 131 | case Kind.Int128: return Type(Int128()); 132 | case Kind.UInt128: return Type(UnsignedInt128()); 133 | 134 | case Kind.Half: return Type(Half()); 135 | case Kind.Float: return Type(Float()); 136 | case Kind.Double: return Type(Double()); 137 | case Kind.LongDouble: return Type(LongDouble()); 138 | case Kind.Float128: return Type(LongDouble()); 139 | 140 | case Kind.Record: 141 | case Kind.Elaborated: // FIXME (could be enum or union) 142 | return Type(UserDefinedType(clangType.spelling.unelaborate)); 143 | } 144 | } 145 | 146 | 147 | private string unelaborate(in string spelling) @safe pure { 148 | import std.array: replace; 149 | return spelling 150 | .replace("struct ", "") 151 | .replace("union ", "") 152 | .replace("enum ", "") 153 | ; 154 | } 155 | -------------------------------------------------------------------------------- /dpp2/source/dpp2/translation/node.d: -------------------------------------------------------------------------------- 1 | module dpp2.translation.node; 2 | 3 | 4 | import dpp.from; 5 | 6 | 7 | string[] translate(in from!"dpp2.sea.node".Node[] nodes) { 8 | import std.algorithm: map; 9 | import std.array: join; 10 | return nodes.map!translate.join; 11 | } 12 | 13 | 14 | string[] translate(in from!"dpp2.sea.node".Node node) 15 | @safe pure 16 | { 17 | import dpp2.sea.node; 18 | import sumtype: match; 19 | 20 | return node.match!( 21 | translateStruct, 22 | translateField, 23 | translateTypedef, 24 | ); 25 | } 26 | 27 | 28 | string[] translateStruct(in from!"dpp2.sea.node".Struct struct_) 29 | @safe pure 30 | { 31 | import dpp2.sea.node: Node; 32 | import std.algorithm: map; 33 | import std.array: array, join; 34 | 35 | string[] lines; 36 | 37 | const spelling = struct_.spelling == "" 38 | ? struct_.typeSpelling 39 | : struct_.spelling; 40 | 41 | lines ~= "struct " ~ spelling; 42 | lines ~= "{"; 43 | 44 | lines ~= struct_ 45 | .nodes 46 | .map!translate 47 | .join 48 | ; 49 | 50 | lines ~= "}"; 51 | 52 | return lines; 53 | } 54 | 55 | 56 | string[] translateField(in from!"dpp2.sea.node".Field field) 57 | @safe pure 58 | { 59 | import dpp2.translation.type: translate; 60 | return [" " ~ translate(field.type) ~ " " ~ field.spelling ~ ";"]; 61 | } 62 | 63 | 64 | string[] translateTypedef(in from!"dpp2.sea.node".Typedef typedef_) 65 | @safe pure 66 | { 67 | import dpp2.translation.type: translate; 68 | const underlyingTranslation = translate(typedef_.underlying); 69 | return typedef_.spelling == underlyingTranslation 70 | ? [] 71 | : ["alias " ~ typedef_.spelling ~ " = " ~ underlyingTranslation ~ ";"]; 72 | } 73 | -------------------------------------------------------------------------------- /dpp2/source/dpp2/translation/type/package.d: -------------------------------------------------------------------------------- 1 | module dpp2.translation.type; 2 | 3 | 4 | import dpp.from; 5 | 6 | 7 | /** 8 | Translate a C/C++ type to D. 9 | */ 10 | // FIXME - const parameter 11 | string translate(from!"dpp2.sea.type".Type type) @safe pure { 12 | import dpp2.sea.type; // too many to list for the pattern match 13 | import sumtype: match; 14 | import std.conv: text; 15 | 16 | return type.match!( 17 | (Void _) => "void", 18 | (NullPointerT _) => "void*", 19 | (Bool _) => "bool", 20 | (UnsignedChar _) => "ubyte", 21 | (SignedChar _) => "byte", 22 | (Char _) => "char", 23 | (Wchar _) => translateWchar(), 24 | (Char16 _) => "wchar", 25 | (Char32 _) => "dchar", 26 | (Short _) => "short", 27 | (UnsignedShort _) => "ushort", 28 | (Int _) => "int", 29 | (UnsignedInt _) => "uint", 30 | (Long _) => "c_long", 31 | (UnsignedLong _) => "c_ulong", 32 | (LongLong _) => "long", 33 | (UnsignedLongLong _) => "ulong", 34 | (Int128 _) => "Int128", 35 | (UnsignedInt128 _) => "UInt128", 36 | (Half _) => "float", 37 | (Float _) => "float", 38 | (Double _) => "double", 39 | (LongDouble _) => "real", 40 | (Pointer ptr) => text(translate(*ptr.pointeeType), "*"), 41 | (ConstantArray arr) => text(translate(*arr.elementType), "[", arr.length, "]"), 42 | (UserDefinedType t) => t.spelling, 43 | ); 44 | } 45 | 46 | 47 | private string translateWchar() @safe pure { 48 | version(Windows) 49 | return "wchar"; 50 | else 51 | return "dchar"; 52 | } 53 | -------------------------------------------------------------------------------- /dpp2/tests/ut/package.d: -------------------------------------------------------------------------------- 1 | module ut; 2 | 3 | public import unit_threaded; 4 | public import std.conv: text; 5 | -------------------------------------------------------------------------------- /dpp2/tests/ut/transform/clang.d: -------------------------------------------------------------------------------- 1 | /** 2 | Tests that we're using libclang properly. 3 | */ 4 | module ut.transform.clang; 5 | 6 | 7 | import ut; 8 | import dpp2.expansion; 9 | import dpp.runtime.options: Options; 10 | import dpp.runtime.context: Context, Language; 11 | 12 | 13 | @("includePaths") 14 | @safe pure unittest { 15 | { 16 | Options options; 17 | options.includePaths = ["foo", "bar"]; 18 | includePaths(options, "dir/app.dpp").should == ["foo", "bar", "dir"]; 19 | includePaths(options, "other/app.dpp").should == ["foo", "bar", "other"]; 20 | } 21 | 22 | { 23 | Options options; 24 | options.includePaths = ["quux"]; 25 | includePaths(options, "dir/app.dpp").should == ["quux", "dir"]; 26 | includePaths(options, "other/app.dpp").should == ["quux", "other"]; 27 | } 28 | } 29 | 30 | 31 | @("clangArgs.c.0") 32 | @safe pure unittest { 33 | Options options; 34 | options.includePaths = ["foo", "bar"]; 35 | options.defines = ["quux", "toto"]; 36 | Context context; 37 | context.options = options; 38 | 39 | clangArgs(context, "dir/app.dpp").shouldBeSameSetAs( 40 | ["-Ifoo", "-Ibar", "-Idir", "-Dquux", "-Dtoto", "-xc"]); 41 | } 42 | 43 | 44 | @("clangArgs.c.1") 45 | @safe pure unittest { 46 | Options options; 47 | options.includePaths = ["fizz"]; 48 | options.defines = ["oops"]; 49 | Context context; 50 | context.options = options; 51 | 52 | clangArgs(context, "other/app.dpp").shouldBeSameSetAs( 53 | ["-Ifizz", "-Iother", "-Doops", "-xc"]); 54 | } 55 | 56 | 57 | @("clangArgs.cpp.parseAsCpp") 58 | @safe pure unittest { 59 | Options options; 60 | options.includePaths = ["fizz"]; 61 | options.defines = ["oops"]; 62 | options.parseAsCpp = true; 63 | Context context; 64 | context.options = options; 65 | 66 | clangArgs(context, "other/app.dpp").shouldBeSameSetAs( 67 | ["-Ifizz", "-Iother", "-Doops", "-xc++", "-std=c++14"]); 68 | } 69 | 70 | @("clangArgs.cpp.language") 71 | @safe pure unittest { 72 | Options options; 73 | options.includePaths = ["fizz"]; 74 | options.defines = ["oops"]; 75 | Context context; 76 | context.options = options; 77 | context.language = Language.Cpp; 78 | 79 | clangArgs(context, "other/app.dpp").shouldBeSameSetAs( 80 | ["-Ifizz", "-Iother", "-Doops", "-xc++", "-std=c++14"]); 81 | } 82 | 83 | 84 | @("getHeaderName") 85 | @safe pure unittest { 86 | import unit_threaded: shouldEqual; 87 | getHeaderName(`#include "foo.h"`).shouldEqual(`foo.h`); 88 | getHeaderName(`#include "bar.h"`).shouldEqual(`bar.h`); 89 | getHeaderName(`#include "foo.h" // comment`).shouldEqual(`foo.h`); 90 | getHeaderName(`#include `).shouldEqual(`foo.h`); 91 | getHeaderName(` #include "foo.h"`).shouldEqual(`foo.h`); 92 | } 93 | -------------------------------------------------------------------------------- /dpp2/tests/ut/transform/cursor.d: -------------------------------------------------------------------------------- 1 | /** 2 | From clang.Cursor to dpp.node.Node. 3 | The tests can't be pure because clang.Cursor.children isn't. 4 | */ 5 | module ut.transform.cursor; 6 | 7 | 8 | import ut.transform; 9 | import clang: Cursor, ClangType = Type; 10 | import dpp2.transform: toNode; 11 | 12 | 13 | @("struct.onefield.int") 14 | @safe unittest { 15 | const tu = mockTU!(Module("contract.aggregates"), 16 | CodeURL("it.c.compile.struct_", "onefield.int")); 17 | 18 | const actual = tu.toNode; 19 | const expected = [Node( 20 | Struct( 21 | "Foo", 22 | [ 23 | Node(Field(Type(Int()), "i")), 24 | ], 25 | "struct Foo", 26 | ) 27 | )]; 28 | 29 | () @trusted { actual.should == expected; }(); 30 | } 31 | 32 | 33 | @("struct.nested") 34 | @safe unittest { 35 | const tu = mockTU!(Module("contract.aggregates"), 36 | CodeURL("it.c.compile.struct_", "nested")); 37 | 38 | const actual = tu.toNode; 39 | const expected = [Node( 40 | Struct( 41 | "Outer", 42 | [ 43 | Node(Field(Type(Int()), "integer")), 44 | Node(Struct("Inner", [ Node(Field(Type(Int()), "x")) ], "struct Inner")), 45 | Node(Field(Type(UserDefinedType("Inner")), "inner")), 46 | ], 47 | "struct Outer", 48 | ) 49 | )]; 50 | 51 | () @trusted { actual.should == expected; }(); 52 | } 53 | 54 | 55 | @("struct.typedef.name") 56 | @safe unittest { 57 | const tu = mockTU!(Module("contract.aggregates"), 58 | CodeURL("it.c.compile.struct_", "typedef.name")); 59 | 60 | const actual = tu.toNode; 61 | const expected = [ 62 | Node(Struct("TypeDefd_", [], "struct TypeDefd_")), 63 | Node(Typedef("TypeDefd", Type(UserDefinedType("TypeDefd_")))), 64 | ]; 65 | 66 | () @trusted { actual.should == expected; }(); 67 | } 68 | 69 | 70 | @("struct.typedef.anon") 71 | @safe unittest { 72 | const tu = mockTU!(Module("contract.aggregates"), 73 | CodeURL("it.c.compile.struct_", "typedef.anon")); 74 | 75 | const actual = tu.toNode; 76 | const expected = [ 77 | Node( 78 | Struct( 79 | "", 80 | [ 81 | Node(Field(Type(Int()), "x")), 82 | Node(Field(Type(Int()), "y")), 83 | Node(Field(Type(Int()), "z")), 84 | ], 85 | "Nameless1", 86 | ), 87 | ), 88 | Node(Typedef("Nameless1", Type(UserDefinedType("Nameless1")))), 89 | Node( 90 | Struct( 91 | "", 92 | [ 93 | Node(Field(Type(Double()), "d")), 94 | ], 95 | "Nameless2", 96 | ), 97 | ), 98 | Node(Typedef("Nameless2", Type(UserDefinedType("Nameless2")))), 99 | ]; 100 | 101 | 102 | () @trusted { actual.should == expected; }(); 103 | } 104 | 105 | 106 | @("struct.typedef.before") 107 | @safe unittest { 108 | const tu = mockTU!(Module("contract.aggregates"), 109 | CodeURL("it.c.compile.struct_", "typedef.before")); 110 | const actual = tu.toNode; 111 | const expected = [ 112 | Node(Typedef("B", Type(UserDefinedType("A")))), 113 | Node(Struct("A", [Node(Field(Type(Int()), "a"))], "struct A")), 114 | ]; 115 | 116 | () @trusted { actual.should == expected; }(); 117 | } 118 | -------------------------------------------------------------------------------- /dpp2/tests/ut/transform/package.d: -------------------------------------------------------------------------------- 1 | module ut.transform; 2 | 3 | 4 | public import ut; 5 | public import contract: mockTU, Module, CodeURL; 6 | public import dpp2.sea.node; 7 | public import dpp2.sea.type; 8 | -------------------------------------------------------------------------------- /dpp2/tests/ut/transform/type.d: -------------------------------------------------------------------------------- 1 | module ut.transform.type; 2 | 3 | 4 | import ut.transform; 5 | import dpp2.transform: toType; 6 | import clang: ClangType = Type; 7 | 8 | alias Kind = ClangType.Kind; 9 | 10 | 11 | @("void") 12 | @safe pure unittest { 13 | ClangType(Kind.Void).toType.should == Type(Void()); 14 | } 15 | 16 | 17 | @("nullptr") 18 | @safe pure unittest { 19 | ClangType(Kind.NullPtr).toType.should == Type(NullPointerT()); 20 | } 21 | 22 | 23 | @("bool") 24 | @safe pure unittest { 25 | ClangType(Kind.Bool).toType.should == Type(Bool()); 26 | } 27 | 28 | 29 | @("wchar") 30 | @safe pure unittest { 31 | ClangType(Kind.WChar).toType.should == Type(Wchar()); 32 | } 33 | 34 | 35 | @("schar") 36 | @safe pure unittest { 37 | ClangType(Kind.SChar).toType.should == Type(SignedChar()); 38 | } 39 | 40 | 41 | @("char16") 42 | @safe pure unittest { 43 | ClangType(Kind.Char16).toType.should == Type(Char16()); 44 | } 45 | 46 | 47 | @("char32") 48 | @safe pure unittest { 49 | ClangType(Kind.Char32).toType.should == Type(Char32()); 50 | } 51 | 52 | 53 | @("uchar") 54 | @safe pure unittest { 55 | ClangType(Kind.UChar).toType.should == Type(UnsignedChar()); 56 | } 57 | 58 | 59 | @("char_u") 60 | @safe pure unittest { 61 | ClangType(Kind.Char_U).toType.should == Type(UnsignedChar()); 62 | } 63 | 64 | 65 | @("char_s") 66 | @safe pure unittest { 67 | ClangType(Kind.Char_S).toType.should == Type(SignedChar()); 68 | } 69 | 70 | 71 | @("ushort") 72 | @safe pure unittest { 73 | ClangType(Kind.UShort).toType.should == Type(UnsignedShort()); 74 | } 75 | 76 | 77 | @("short") 78 | @safe pure unittest { 79 | ClangType(Kind.Short).toType.should == Type(Short()); 80 | } 81 | 82 | 83 | @("int") 84 | @safe pure unittest { 85 | ClangType(Kind.Int).toType.should == Type(Int()); 86 | } 87 | 88 | 89 | @("uint") 90 | @safe pure unittest { 91 | ClangType(Kind.UInt).toType.should == Type(UnsignedInt()); 92 | } 93 | 94 | 95 | @("long") 96 | @safe pure unittest { 97 | ClangType(Kind.Long).toType.should == Type(Long()); 98 | } 99 | 100 | 101 | @("ulong") 102 | @safe pure unittest { 103 | ClangType(Kind.ULong).toType.should == Type(UnsignedLong()); 104 | } 105 | 106 | 107 | @("longlong") 108 | @safe pure unittest { 109 | ClangType(Kind.LongLong).toType.should == Type(LongLong()); 110 | } 111 | 112 | 113 | @("ulonglong") 114 | @safe pure unittest { 115 | ClangType(Kind.ULongLong).toType.should == Type(UnsignedLongLong()); 116 | } 117 | 118 | 119 | @("int128") 120 | @safe pure unittest { 121 | ClangType(Kind.Int128).toType.should == Type(Int128()); 122 | } 123 | 124 | 125 | @("uint128") 126 | @safe pure unittest { 127 | ClangType(Kind.UInt128).toType.should == Type(UnsignedInt128()); 128 | } 129 | 130 | 131 | @("float") 132 | @safe pure unittest { 133 | ClangType(Kind.Float).toType.should == Type(Float()); 134 | } 135 | 136 | 137 | @("double") 138 | @safe pure unittest { 139 | ClangType(Kind.Double).toType.should == Type(Double()); 140 | } 141 | 142 | 143 | @("float128") 144 | @safe pure unittest { 145 | ClangType(Kind.Float128).toType.should == Type(LongDouble()); 146 | } 147 | 148 | 149 | @("half") 150 | @safe pure unittest { 151 | ClangType(Kind.Half).toType.should == Type(Half()); 152 | } 153 | 154 | 155 | @("longdouble") 156 | @safe pure unittest { 157 | ClangType(Kind.LongDouble).toType.should == Type(LongDouble()); 158 | } 159 | 160 | 161 | @("record") 162 | @safe pure unittest { 163 | ClangType(Kind.Record, "mytype").toType.should == Type(UserDefinedType("mytype")); 164 | } 165 | -------------------------------------------------------------------------------- /dpp2/tests/ut/translation/node/package.d: -------------------------------------------------------------------------------- 1 | module ut.translation.node; 2 | 3 | 4 | public import ut; 5 | public import dpp2.translation.node; 6 | public import dpp2.sea.node; 7 | public import dpp2.sea.type; 8 | -------------------------------------------------------------------------------- /dpp2/tests/ut/translation/node/structs.d: -------------------------------------------------------------------------------- 1 | module ut.translation.node.structs; 2 | 3 | 4 | import ut.translation.node; 5 | import std.array: join; 6 | 7 | 8 | @("onefield.int") 9 | @safe pure unittest { 10 | enum node = Node( 11 | Struct( 12 | "Foo", 13 | [ 14 | Node(Field(Type(Int()), "i")), 15 | ] 16 | ) 17 | ); 18 | 19 | enum translation = translate(node); 20 | writelnUt(translation); 21 | mixin(`static ` ~ translation.join("\n")); 22 | 23 | static assert(Foo.tupleof.length == 1); 24 | static assert(is(typeof(Foo.i) == int)); 25 | } 26 | 27 | 28 | @("onefield.double") 29 | @safe pure unittest { 30 | enum node = Node( 31 | Struct( 32 | "Bar", 33 | [ 34 | Node(Field(Type(Double()), "d")), 35 | ] 36 | ) 37 | ); 38 | 39 | enum translation = translate(node); 40 | writelnUt(translation); 41 | mixin(`static ` ~ translation.join("\n")); 42 | 43 | static assert(Bar.tupleof.length == 1); 44 | static assert(is(typeof(Bar.d) == double)); 45 | } 46 | 47 | 48 | @("twofields") 49 | @safe pure unittest { 50 | enum node = Node( 51 | Struct( 52 | "Baz", 53 | [ 54 | Node(Field(Type(Int()), "i",)), 55 | Node(Field(Type(Double()), "d",)), 56 | ] 57 | ) 58 | ); 59 | 60 | enum translation = translate(node); 61 | writelnUt(translation); 62 | mixin(`static ` ~ translation.join("\n")); 63 | 64 | static assert(Baz.tupleof.length == 2); 65 | static assert(is(typeof(Baz.i) == int)); 66 | static assert(is(typeof(Baz.d) == double)); 67 | } 68 | 69 | 70 | @("typedef.name") 71 | @safe pure unittest { 72 | enum nodes = [ 73 | Node(Struct("TypeDefd_", [])), 74 | Node(Typedef("TypeDefd", Type(UserDefinedType("TypeDefd_")))), 75 | ]; 76 | enum translation = translate(nodes); 77 | writelnUt(translation); 78 | mixin(translation.join("\n")); 79 | 80 | static assert(is(TypeDefd)); 81 | static assert(is(TypeDefd_)); 82 | } 83 | 84 | 85 | @("typedef.anon") 86 | @safe pure unittest { 87 | enum nodes = [ 88 | Node( 89 | Struct( 90 | "", 91 | [ 92 | Node(Field(Type(Int()), "x")), 93 | Node(Field(Type(Int()), "y")), 94 | Node(Field(Type(Int()), "z")), 95 | ], 96 | "Nameless1", // type spelling 97 | ), 98 | ), 99 | Node(Typedef("Nameless1", Type(UserDefinedType("Nameless1")))), 100 | Node( 101 | Struct( 102 | "", 103 | [ 104 | Node(Field(Type(Double()), "d")), 105 | ], 106 | "Nameless2", // type spelling 107 | ), 108 | ), 109 | Node(Typedef("Nameless2", Type(UserDefinedType("Nameless2")))), 110 | ]; 111 | 112 | enum translation = "\n" ~ translate(nodes).join("\n"); 113 | writelnUt(translation); 114 | mixin(translation); 115 | 116 | static assert(is(Nameless1), translation); 117 | static assert(is(typeof(Nameless1.x) == int), translation); 118 | static assert(is(typeof(Nameless1.y) == int), translation); 119 | static assert(is(typeof(Nameless1.z) == int), translation); 120 | 121 | static assert(is(Nameless2), translation); 122 | static assert(is(typeof(Nameless2.d) == double), translation); 123 | } 124 | 125 | @("typedef.before") 126 | @safe pure unittest { 127 | 128 | // unfortunately, order matters in unit test blocks 129 | enum nodes = [ 130 | Node(Struct("A", [Node(Field(Type(Int()), "a"))], "struct A")), 131 | Node(Typedef("B", Type(UserDefinedType("A")))), 132 | ]; 133 | 134 | enum translation = "\n" ~ translate(nodes).join("\n"); 135 | writelnUt(translation); 136 | mixin(translation); 137 | 138 | static assert(is(A == B)); 139 | static assert(is(A == struct)); 140 | static assert(A.tupleof.length == 1); 141 | static assert(is(typeof(A.a) == int)); 142 | } 143 | -------------------------------------------------------------------------------- /dpp2/tests/ut/translation/type/array.d: -------------------------------------------------------------------------------- 1 | module ut.translation.type.array; 2 | 3 | 4 | import ut.translation.type; 5 | 6 | 7 | @("constant int array 4") 8 | @safe pure unittest { 9 | enum translation = translate(Type(ConstantArray(new Type(Int()), 4))); 10 | mixin(translation ~ " arr;"); 11 | static assert(is(typeof(arr) == int[4]), typeof(arr).stringof); 12 | } 13 | 14 | 15 | @("constant int array 5") 16 | @safe pure unittest { 17 | enum translation = translate(Type(ConstantArray(new Type(Int()), 5))); 18 | mixin(translation ~ " arr;"); 19 | static assert(is(typeof(arr) == int[5]), typeof(arr).stringof); 20 | } 21 | 22 | 23 | @("constant long array 6") 24 | @safe pure unittest { 25 | enum translation = translate(Type(ConstantArray(new Type(Long()), 6))); 26 | mixin(translation ~ " arr;"); 27 | static assert(is(typeof(arr) == long[6]), typeof(arr).stringof); 28 | } 29 | -------------------------------------------------------------------------------- /dpp2/tests/ut/translation/type/package.d: -------------------------------------------------------------------------------- 1 | module ut.translation.type; 2 | 3 | public import ut; 4 | public import core.stdc.config; 5 | public import dpp2.sea.type; 6 | public import dpp2.translation.type; 7 | -------------------------------------------------------------------------------- /dpp2/tests/ut/translation/type/pointer.d: -------------------------------------------------------------------------------- 1 | module ut.translation.type.pointer; 2 | 3 | 4 | import ut.translation.type; 5 | 6 | 7 | @("int*") 8 | @safe pure unittest { 9 | enum translation = translate(Type(Pointer(new Type(Int())))); 10 | mixin(translation ~ " x;"); 11 | static assert(is(typeof(x) == int*), typeof(x).stringof); 12 | } 13 | 14 | @("int**") 15 | @safe pure unittest { 16 | enum translation = translate(Type(Pointer(new Type(Pointer(new Type(Int())))))); 17 | mixin(translation ~ " x;"); 18 | static assert(is(typeof(x) == int**), typeof(x).stringof); 19 | } 20 | 21 | @("double*") 22 | @safe pure unittest { 23 | enum translation = translate(Type(Pointer(new Type(Double())))); 24 | mixin(translation ~ " x;"); 25 | static assert(is(typeof(x) == double*), typeof(x).stringof); 26 | } 27 | -------------------------------------------------------------------------------- /dpp2/tests/ut/translation/type/primitives.d: -------------------------------------------------------------------------------- 1 | module ut.translation.type.primitives; 2 | 3 | 4 | import ut.translation.type; 5 | 6 | 7 | @("void") 8 | @safe pure unittest { 9 | enum translation = translate(Type(Void())); 10 | mixin(translation ~ "[] x;"); 11 | static assert(is(typeof(x) == void[]), typeof(x).stringof); 12 | } 13 | 14 | @("nullptr_t") 15 | @safe pure unittest { 16 | enum translation = translate(Type(NullPointerT())); 17 | mixin(translation ~ " x;"); 18 | static assert(is(typeof(x) == void*), typeof(x).stringof); 19 | } 20 | 21 | @("bool") 22 | @safe pure unittest { 23 | enum translation = translate(Type(Bool())); 24 | mixin(translation ~ " x;"); 25 | static assert(is(typeof(x) == bool), typeof(x).stringof); 26 | } 27 | 28 | @("unsigned char") 29 | @safe pure unittest { 30 | enum translation = translate(Type(UnsignedChar())); 31 | mixin(translation ~ " x;"); 32 | static assert(is(typeof(x) == ubyte), typeof(x).stringof); 33 | } 34 | 35 | @("signed char") 36 | @safe pure unittest { 37 | enum translation = translate(Type(SignedChar())); 38 | mixin(translation ~ " x;"); 39 | static assert(is(typeof(x) == byte), typeof(x).stringof); 40 | } 41 | 42 | @("char") 43 | @safe pure unittest { 44 | enum translation = translate(Type(Char())); 45 | mixin(translation ~ " x;"); 46 | static assert(is(typeof(x) == char), typeof(x).stringof); 47 | } 48 | 49 | @("char16_t") 50 | @safe pure unittest { 51 | enum translation = translate(Type(Char16())); 52 | mixin(translation ~ " x;"); 53 | static assert(is(typeof(x) == wchar), typeof(x).stringof); 54 | } 55 | 56 | @("char32_t") 57 | @safe pure unittest { 58 | enum translation = translate(Type(Char32())); 59 | mixin(translation ~ " x;"); 60 | static assert(is(typeof(x) == dchar), typeof(x).stringof); 61 | } 62 | 63 | @("wchar_t") 64 | @safe pure unittest { 65 | enum translation = translate(Type(Wchar())); 66 | mixin(translation ~ " x;"); 67 | version(Windows) 68 | static assert(is(typeof(x) == wchar), typeof(x).stringof); 69 | else 70 | static assert(is(typeof(x) == dchar), typeof(x).stringof); 71 | } 72 | 73 | @("short") 74 | @safe pure unittest { 75 | enum translation = translate(Type(Short())); 76 | mixin(translation ~ " x;"); 77 | static assert(is(typeof(x) == short), typeof(x).stringof); 78 | } 79 | 80 | @("ushort") 81 | @safe pure unittest { 82 | enum translation = translate(Type(UnsignedShort())); 83 | mixin(translation ~ " x;"); 84 | static assert(is(typeof(x) == ushort), typeof(x).stringof); 85 | } 86 | 87 | @("int") 88 | @safe pure unittest { 89 | enum translation = translate(Type(Int())); 90 | mixin(translation ~ " x;"); 91 | static assert(is(typeof(x) == int), typeof(x).stringof); 92 | } 93 | 94 | @("uint") 95 | @safe pure unittest { 96 | enum translation = translate(Type(UnsignedInt())); 97 | mixin(translation ~ " x;"); 98 | static assert(is(typeof(x) == uint), typeof(x).stringof); 99 | } 100 | 101 | @("long") 102 | @safe pure unittest { 103 | enum translation = translate(Type(Long())); 104 | mixin(translation ~ " x;"); 105 | static assert(is(typeof(x) == long), typeof(x).stringof); 106 | } 107 | 108 | @("ulong") 109 | @safe pure unittest { 110 | enum translation = translate(Type(UnsignedLong())); 111 | mixin(translation ~ " x;"); 112 | static assert(is(typeof(x) == ulong), typeof(x).stringof); 113 | } 114 | 115 | @("longlong") 116 | @safe pure unittest { 117 | enum translation = translate(Type(LongLong())); 118 | mixin(translation ~ " x;"); 119 | static assert(is(typeof(x) == long), typeof(x).stringof); 120 | } 121 | 122 | @("ulonglong") 123 | @safe pure unittest { 124 | enum translation = translate(Type(UnsignedLongLong())); 125 | mixin(translation ~ " x;"); 126 | static assert(is(typeof(x) == ulong), typeof(x).stringof); 127 | } 128 | 129 | @("float") 130 | @safe pure unittest { 131 | enum translation = translate(Type(Float())); 132 | mixin(translation ~ " x;"); 133 | static assert(is(typeof(x) == float), typeof(x).stringof); 134 | } 135 | 136 | @("double") 137 | @safe pure unittest { 138 | enum translation = translate(Type(Double())); 139 | mixin(translation ~ " x;"); 140 | static assert(is(typeof(x) == double), typeof(x).stringof); 141 | } 142 | 143 | @("long double") 144 | @safe pure unittest { 145 | enum translation = translate(Type(LongDouble())); 146 | mixin(translation ~ " x;"); 147 | static assert(is(typeof(x) == real), typeof(x).stringof); 148 | } 149 | -------------------------------------------------------------------------------- /dub.sdl: -------------------------------------------------------------------------------- 1 | name "dpp" 2 | description "Include C/C++ headers directly in D files" 3 | authors "Atila Neves" 4 | copyright "Copyright © 2017-2018, Atila Neves" 5 | license "boost" 6 | 7 | targetType "executable" 8 | targetPath "bin" 9 | targetName "d++" 10 | 11 | dependency "libclang" version="~>0.3.1" 12 | 13 | versions "SumTypeNoDefaultCtor" 14 | 15 | 16 | buildType "release" { 17 | buildOptions "releaseMode" "optimize" "inline" "debugInfo" 18 | } 19 | 20 | buildType "profilec" { 21 | buildOptions "profile" "releaseMode" "optimize" "inline" "debugInfo" 22 | } 23 | 24 | buildType "profile-gc" { 25 | buildOptions "profileGC" "releaseMode" "optimize" "inline" "debugInfo" 26 | } 27 | 28 | 29 | configuration "executable" { 30 | dflags "-dip1000" 31 | mainSourceFile "source/main.d" 32 | } 33 | 34 | 35 | configuration "library" { 36 | targetType "library" 37 | targetName "dpp" 38 | excludedSourceFiles "source/main.d" 39 | } 40 | 41 | 42 | configuration "unittest" { 43 | targetName "all_tests" 44 | mainSourceFile "tests/test_main.d" 45 | sourcePaths "tests" 46 | importPaths "tests" 47 | excludedSourceFiles "source/main.d" "tests/contract/main.d" "tests/it/main.d" 48 | dependency "unit-threaded" version="*" 49 | } 50 | 51 | 52 | configuration "integration" { 53 | targetName "it" 54 | mainSourceFile "tests/it/main.d" 55 | sourcePaths "tests/it" "tests/common" 56 | importPaths "tests" 57 | 58 | excludedSourceFiles "source/main.d" 59 | dependency "unit-threaded" version="*" 60 | } 61 | 62 | 63 | configuration "contract" { 64 | targetName "ct" 65 | mainSourceFile "tests/contract/main.d" 66 | sourcePaths "tests/contract" "tests/common" 67 | importPaths "tests" 68 | 69 | excludedSourceFiles "source/main.d" 70 | dependency "unit-threaded" version="*" 71 | } 72 | 73 | 74 | configuration "dpp2" { 75 | targetName "dpp2" 76 | mainSourceFile "tests/test_main.d" 77 | sourcePaths "dpp2/source" "dpp2/tests" "tests" 78 | importPaths "dpp2/source" "dpp2/tests" "tests" 79 | excludedSourceFiles "source/main.d" "tests/ut/package.d" 80 | dependency "unit-threaded" version="*" 81 | versions "dpp2" 82 | } 83 | -------------------------------------------------------------------------------- /dub.selections.json: -------------------------------------------------------------------------------- 1 | { 2 | "fileVersion": 1, 3 | "versions": { 4 | "libclang": "0.3.3", 5 | "unit-threaded": "2.1.9" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /examples/.gitignore: -------------------------------------------------------------------------------- 1 | *.d 2 | *.tmp -------------------------------------------------------------------------------- /examples/all.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Check all the examples work 4 | 5 | set -euo pipefail 6 | 7 | SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 8 | BIN_DIR="$SCRIPT_DIR"/../bin 9 | 10 | # shellcheck source=../bash/funcs.bash 11 | source "$SCRIPT_DIR/../bash/funcs.bash" 12 | 13 | clear 14 | build_dpp 15 | echo 16 | 17 | # Files that should compile with no dependencies 18 | "$SCRIPT_DIR"/compile/all.sh 19 | echo 20 | 21 | # Files that should run with no dependencies 22 | "$SCRIPT_DIR"/run/all.sh 23 | echo 24 | 25 | # Files that need downloaded dependencies 26 | "$SCRIPT_DIR"/download/all.sh 27 | echo 28 | -------------------------------------------------------------------------------- /examples/compile/.gitignore: -------------------------------------------------------------------------------- 1 | *.d 2 | *.tmp -------------------------------------------------------------------------------- /examples/compile/all.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 6 | BIN_DIR="$SCRIPT_DIR"/../../bin 7 | 8 | # shellcheck source=../../bash/funcs.bash 9 | source "$SCRIPT_DIR/../../bash/funcs.bash" 10 | 11 | build_dpp 12 | 13 | # Restore when there are files 14 | # for x in "$SCRIPT_DIR"/*.dpp 15 | # do 16 | # echo "Testing compileable $x" 17 | # "$BIN_DIR"/d++ --keep-pre-cpp-files -c -of/tmp/compile.o "$x" 18 | # done 19 | -------------------------------------------------------------------------------- /examples/download/.gitignore: -------------------------------------------------------------------------------- 1 | repos 2 | anatomy 3 | imap 4 | virt 5 | zfs 6 | nanomsg 7 | etpan -------------------------------------------------------------------------------- /examples/download/all.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This script downloads some open source C libraries 4 | # And tests compiling example .dpp files that #include them 5 | 6 | set -euo pipefail 7 | 8 | 9 | SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 10 | REPOS_DIR="$SCRIPT_DIR"/repos 11 | BIN_DIR="$SCRIPT_DIR"/../../bin 12 | 13 | # shellcheck source=../../bash/funcs.bash 14 | source "$SCRIPT_DIR/../../bash/funcs.bash" 15 | 16 | build_dpp 17 | 18 | [[ -e "$REPOS_DIR" ]] || mkdir "$REPOS_DIR" 19 | 20 | pushd "$REPOS_DIR" 21 | 22 | [[ -e "$REPOS_DIR"/libxlsxwriter ]] || git_clone git@github.com:jmcnamara/libxlsxwriter.git 23 | [[ -e "$REPOS_DIR"/libetpan ]] || git_clone git@github.com:dinhviethoa/libetpan.git 24 | [[ -e "$REPOS_DIR"/imapfilter ]] || git_clone git@github.com:lefcha/imapfilter.git 25 | [[ -e "$REPOS_DIR"/libvirt ]] || git_clone git@github.com:libvirt/libvirt.git 26 | [[ -e "$REPOS_DIR"/zfs ]] || git_clone git@github.com:zfsonlinux/zfs.git 27 | [[ -e "$REPOS_DIR"/nanomsg ]] || git_clone git@github.com:nanomsg/nanomsg.git 28 | 29 | 30 | if [[ ! -e "$REPOS_DIR"/libetpan/include/libetpan ]]; then 31 | pushd "$REPOS_DIR"/libetpan 32 | ./autogen.sh 33 | mk 34 | popd 35 | if [[ ! -e "$REPOS_DIR"/libetpan/include/libetpan ]]; then 36 | echo "ERROR: no libetpan" 37 | exit 1 38 | fi 39 | fi 40 | 41 | if [[ ! -e "$REPOS_DIR"/libvirt/config.h ]]; then 42 | pushd "$REPOS_DIR"/libvirt 43 | ./autogen.sh 44 | popd 45 | if [[ ! -e "$REPOS_DIR"/libvirt/config.h ]]; then 46 | echo "ERROR no config.h for libvirt" 47 | exit 1 48 | fi 49 | fi 50 | popd 51 | 52 | dpp() { 53 | "$BIN_DIR"/d++ --keep-d-files "$@" 54 | } 55 | 56 | echo Testing download etpan 57 | dpp --include-path "$REPOS_DIR"/libetpan/include "$SCRIPT_DIR"/etpan.dpp -c 58 | 59 | echo Testing download nanomsg 60 | dpp --include-path "$REPOS_DIR" "$SCRIPT_DIR"/nanomsg.dpp -L-lnanomsg -of/tmp/nanomsg -checkaction=context 61 | /tmp/nanomsg 62 | 63 | echo Testing download libxlsxwriter 64 | dpp --include-path "$REPOS_DIR"/libxlsxwriter/include "$SCRIPT_DIR"/anatomy.dpp -c 65 | 66 | echo Testing download zfs 67 | dpp --include-path "$REPOS_DIR"/zfs/include --include-path "$REPOS_DIR"/zfs/lib/libspl/include "$SCRIPT_DIR"/zfs.dpp -c 68 | 69 | echo Testing download libvirt 70 | dpp --include-path "$REPOS_DIR"/libvirt --include-path "$REPOS_DIR"/libvirt/include "$SCRIPT_DIR"/virt.dpp -c 71 | 72 | if [ ! -z "${TRAVIS-}" ]; then 73 | echo " Skipping imapfilter for Travis CI" 74 | else 75 | echo Testing download imapfilter 76 | dpp --include-path "$REPOS_DIR"/imapfilter/src "$SCRIPT_DIR"/imap.dpp -c 77 | fi 78 | -------------------------------------------------------------------------------- /examples/download/anatomy.dpp: -------------------------------------------------------------------------------- 1 | #include "xlsxwriter.h" 2 | 3 | int main() { 4 | 5 | /* Create a new workbook. */ 6 | lxw_workbook *workbook = workbook_new("anatomy.xlsx"); 7 | 8 | /* Add a worksheet with a user defined sheet name. */ 9 | lxw_worksheet *worksheet1 = workbook_add_worksheet(workbook, "Demo"); 10 | 11 | /* Add a worksheet with Excel's default sheet name: Sheet2. */ 12 | lxw_worksheet *worksheet2 = workbook_add_worksheet(workbook, null); 13 | 14 | /* Add some cell formats. */ 15 | lxw_format *myformat1 = workbook_add_format(workbook); 16 | lxw_format *myformat2 = workbook_add_format(workbook); 17 | 18 | /* Set the bold property for the first format. */ 19 | format_set_bold(myformat1); 20 | 21 | /* Set a number format for the second format. */ 22 | format_set_num_format(myformat2, "$#,##0.00"); 23 | 24 | /* Widen the first column to make the text clearer. */ 25 | worksheet_set_column(worksheet1, 0, 0, 20, null); 26 | 27 | /* Write some unformatted data. */ 28 | worksheet_write_string(worksheet1, 0, 0, "Peach", null); 29 | worksheet_write_string(worksheet1, 1, 0, "Plum", null); 30 | 31 | /* Write formatted data. */ 32 | worksheet_write_string(worksheet1, 2, 0, "Pear", myformat1); 33 | 34 | /* Formats can be reused. */ 35 | worksheet_write_string(worksheet1, 3, 0, "Persimmon", myformat1); 36 | 37 | 38 | /* Write some numbers. */ 39 | worksheet_write_number(worksheet1, 5, 0, 123, null); 40 | worksheet_write_number(worksheet1, 6, 0, 4567.555, myformat2); 41 | 42 | 43 | /* Write to the second worksheet. */ 44 | worksheet_write_string(worksheet2, 0, 0, "Some text", myformat1); 45 | 46 | 47 | /* Close the workbook, save the file and free any memory. */ 48 | lxw_error error = workbook_close(workbook); 49 | 50 | /* Check if there was any error creating the xlsx file. */ 51 | if (error) 52 | printf("Error in workbook_close().\n" ~ 53 | "Error %d = %s\n", error, lxw_strerror(error)); 54 | 55 | return error; 56 | } 57 | -------------------------------------------------------------------------------- /examples/download/etpan.dpp: -------------------------------------------------------------------------------- 1 | #include "libetpan/libetpan.h" 2 | -------------------------------------------------------------------------------- /examples/download/imap.dpp: -------------------------------------------------------------------------------- 1 | #include "imapfilter.h" 2 | -------------------------------------------------------------------------------- /examples/download/nanomsg.dpp: -------------------------------------------------------------------------------- 1 | #include "nanomsg/nn.h" 2 | #include "nanomsg/pipeline.h" 3 | import core.thread: Thread, msecs; 4 | 5 | void main() { 6 | enum uri = "inproc://test"; 7 | 8 | auto pull = nn_socket(AF_SP, NN_PULL); 9 | scope(exit) nn_close(pull); 10 | assert(pull >= 0); 11 | assert(nn_bind(pull, uri) >= 0); 12 | Thread.sleep(50.msecs); 13 | 14 | auto push = nn_socket(AF_SP, NN_PUSH); 15 | scope(exit) nn_close(push); 16 | assert(push >= 0); 17 | assert(nn_connect(push, uri) >= 0); 18 | const ubyte[] bytes = [1, 2, 3]; 19 | assert(nn_send(push, &bytes[0], bytes.length, 0) == bytes.length); 20 | ubyte[1024] buf; 21 | assert(nn_recv(pull, &buf[0], buf.length, 0) == bytes.length); 22 | assert(buf[0 .. bytes.length] == bytes); 23 | } 24 | -------------------------------------------------------------------------------- /examples/download/virt.dpp: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | static int 8 | showHypervisorInfo(virConnectPtr conn) 9 | { 10 | int ret = 0; 11 | ulong hvVer, major, minor, release; 12 | const(char) *hvType; 13 | 14 | /* virConnectGetType returns a pointer to a static string, so no 15 | * allocation or freeing is necessary; it is possible for the call 16 | * to fail if, for example, there is no connection to a 17 | * hypervisor, so check what it returns. */ 18 | hvType = virConnectGetType(conn); 19 | if (!hvType) { 20 | ret = 1; 21 | printf("Failed to get hypervisor type: %s\n", 22 | virGetLastErrorMessage()); 23 | goto out_; 24 | } 25 | 26 | if (0 != virConnectGetVersion(conn, &hvVer)) { 27 | ret = 1; 28 | printf("Failed to get hypervisor version: %s\n", 29 | virGetLastErrorMessage()); 30 | goto out_; 31 | } 32 | 33 | major = hvVer / 1000000; 34 | hvVer %= 1000000; 35 | minor = hvVer / 1000; 36 | release = hvVer % 1000; 37 | 38 | printf("Hypervisor: \"%s\" version: %lu.%lu.%lu\n", 39 | hvType, 40 | major, 41 | minor, 42 | release); 43 | 44 | out_: 45 | return ret; 46 | } 47 | 48 | 49 | static int 50 | showDomains(virConnectPtr conn) 51 | { 52 | int ret = 0, numNames, numInactiveDomains, numActiveDomains; 53 | ssize_t i; 54 | int flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE | 55 | VIR_CONNECT_LIST_DOMAINS_INACTIVE; 56 | virDomainPtr *nameList = null; 57 | 58 | /* NB: The return from the virConnectNum*() APIs is only useful for 59 | * the current call. A domain could be started or stopped and any 60 | * assumptions made purely on these return values could result in 61 | * unexpected results */ 62 | numActiveDomains = virConnectNumOfDomains(conn); 63 | if (numActiveDomains == -1) { 64 | ret = 1; 65 | printf("Failed to get number of active domains: %s\n", 66 | virGetLastErrorMessage()); 67 | goto out_; 68 | } 69 | 70 | numInactiveDomains = virConnectNumOfDefinedDomains(conn); 71 | if (numInactiveDomains == -1) { 72 | ret = 1; 73 | printf("Failed to get number of inactive domains: %s\n", 74 | virGetLastErrorMessage()); 75 | goto out_; 76 | } 77 | 78 | printf("There are %d active and %d inactive domains\n", 79 | numActiveDomains, numInactiveDomains); 80 | 81 | /* Return a list of all active and inactive domains. Using this API 82 | * instead of virConnectListDomains() and virConnectListDefinedDomains() 83 | * is preferred since it "solves" an inherit race between separated API 84 | * calls if domains are started or stopped between calls */ 85 | numNames = virConnectListAllDomains(conn, 86 | &nameList, 87 | flags); 88 | if (numNames == -1) { 89 | ret = 1; 90 | printf("Failed to get a list of all domains: %s\n", 91 | virGetLastErrorMessage()); 92 | goto out_; 93 | } 94 | 95 | for (i = 0; i < numNames; i++) { 96 | int active = virDomainIsActive(nameList[i]); 97 | printf(" %8s (%s)\n", 98 | virDomainGetName(nameList[i]), 99 | (active == 1 ? "active".ptr : "non-active".ptr)); 100 | /* must free the returned named per the API documentation */ 101 | virDomainFree(nameList[i]); 102 | } 103 | free(nameList); 104 | 105 | out_: 106 | return ret; 107 | } 108 | 109 | 110 | int 111 | main(string[] args) 112 | { 113 | import std.string: toStringz; 114 | int ret = 0; 115 | virConnectPtr conn; 116 | char *uri; 117 | 118 | 119 | printf("Attempting to connect to hypervisor\n"); 120 | 121 | uri = cast(char*) (args.length > 0 ? args[1] : "").toStringz; 122 | 123 | /* virConnectOpenAuth is called here with all default parameters, 124 | * except, possibly, the URI of the hypervisor. */ 125 | conn = virConnectOpenAuth(uri, virConnectAuthPtrDefault, 0); 126 | 127 | if (!conn) { 128 | ret = 1; 129 | printf("No connection to hypervisor: %s\n", 130 | virGetLastErrorMessage()); 131 | goto out_; 132 | } 133 | 134 | uri = virConnectGetURI(conn); 135 | if (!uri) { 136 | ret = 1; 137 | printf("Failed to get URI for hypervisor connection: %s\n", 138 | virGetLastErrorMessage()); 139 | goto disconnect; 140 | } 141 | 142 | printf("Connected to hypervisor at \"%s\"\n", uri); 143 | free(uri); 144 | 145 | if (0 != showHypervisorInfo(conn)) { 146 | ret = 1; 147 | goto disconnect; 148 | } 149 | 150 | if (0 != showDomains(conn)) { 151 | ret = 1; 152 | goto disconnect; 153 | } 154 | 155 | disconnect: 156 | if (0 != virConnectClose(conn)) { 157 | printf("Failed to disconnect from hypervisor: %s\n", 158 | virGetLastErrorMessage()); 159 | ret = 1; 160 | } else { 161 | printf("Disconnected from hypervisor\n"); 162 | } 163 | 164 | out_: 165 | return ret; 166 | } 167 | -------------------------------------------------------------------------------- /examples/download/zfs.dpp: -------------------------------------------------------------------------------- 1 | #include "libzfs.h" 2 | 3 | void code() { 4 | // issue #11 5 | nvlist_prtctl_t foo; 6 | foo = null; 7 | } 8 | -------------------------------------------------------------------------------- /examples/run/all.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 6 | BIN_DIR="$SCRIPT_DIR"/../../bin 7 | 8 | # shellcheck source=../../bash/funcs.bash 9 | source "$SCRIPT_DIR/../../bash/funcs.bash" 10 | 11 | build_dpp 12 | 13 | for x in "$SCRIPT_DIR"/*.dpp 14 | do 15 | filename=$(basename -- "$x") 16 | name="${filename%.*}" 17 | 18 | if [[ "$name" == "openssl" ]] && [ ! -z "${TRAVIS-}" ]; then 19 | echo " Skipping $name for Travis CI" 20 | else 21 | echo "Testing runnable $name" 22 | "$SCRIPT_DIR"/run.sh "$name" > /dev/null 23 | fi 24 | done 25 | -------------------------------------------------------------------------------- /examples/run/curl.dpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | * _ _ ____ _ 3 | * Project ___| | | | _ \| | 4 | * / __| | | | |_) | | 5 | * | (__| |_| | _ <| |___ 6 | * \___|\___/|_| \_\_____| 7 | * 8 | * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. 9 | * 10 | * This software is licensed as described in the file COPYING, which 11 | * you should have received as part of this distribution. The terms 12 | * are also available at https://curl.haxx.se/docs/copyright.html. 13 | * 14 | * You may opt to use, copy, modify, merge, publish, distribute and/or sell 15 | * copies of the Software, and permit persons to whom the Software is 16 | * furnished to do so, under the terms of the COPYING file. 17 | * 18 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 | * KIND, either express or implied. 20 | * 21 | ***************************************************************************/ 22 | /* 23 | * Very simple HTTP GET 24 | * 25 | */ 26 | #include 27 | 28 | void main() 29 | { 30 | import std.string: toStringz; 31 | import std.conv: text; 32 | import std.stdio: stderr; 33 | 34 | CURL *curl; 35 | CURLcode res; 36 | 37 | curl = curl_easy_init(); 38 | if(curl) { 39 | curl_easy_setopt_(curl, CURLOPT_URL, "http://example.com".toStringz); 40 | /* example.com is redirected, so we tell libcurl to follow redirection */ 41 | curl_easy_setopt_(curl, CURLOPT_FOLLOWLOCATION, 1L); 42 | 43 | /* Perform the request, res will get the return code */ 44 | res = curl_easy_perform(curl); 45 | /* Check for errors */ 46 | if(res != CURLE_OK) 47 | stderr.writeln("curl_easy_perform() failed: ", curl_easy_strerror(res).text); 48 | 49 | /* always cleanup */ 50 | curl_easy_cleanup(curl); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /examples/run/openssl.dpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void main() { 4 | 5 | } -------------------------------------------------------------------------------- /examples/run/pthread.dpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /* this function is run by the second thread */ 5 | extern(C) void* inc_x(void *x_void_ptr) { 6 | 7 | /* increment x to 100 */ 8 | int *x_ptr = cast(int *)x_void_ptr; 9 | while(++(*x_ptr) < 100) {} 10 | 11 | printf("x increment finished\n"); 12 | 13 | /* the function must return something - null will do */ 14 | return null; 15 | } 16 | 17 | int main() { 18 | 19 | int x = 0, y = 0; 20 | 21 | /* show the initial values of x and y */ 22 | printf("x: %d, y: %d\n", x, y); 23 | 24 | /* this variable is our reference to the second thread */ 25 | pthread_t inc_x_thread; 26 | 27 | /* create a second thread which executes inc_x(&x) */ 28 | if(pthread_create(&inc_x_thread, null, &inc_x, &x)) { 29 | 30 | fprintf(stderr, "Error creating thread\n"); 31 | return 1; 32 | 33 | } 34 | /* increment y to 100 in the first thread */ 35 | while(++y < 100) {} 36 | 37 | printf("y increment finished\n"); 38 | 39 | /* wait for the second thread to finish */ 40 | if(pthread_join(inc_x_thread, null)) { 41 | fprintf(stderr, "Error joining thread\n"); 42 | return 2; 43 | } 44 | 45 | return 0; 46 | } 47 | -------------------------------------------------------------------------------- /examples/run/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 6 | BIN_DIR="$SCRIPT_DIR"/../../bin 7 | 8 | "$BIN_DIR"/d++ -of"$BIN_DIR/$1" "$SCRIPT_DIR/$1.dpp" -L-lcurl 9 | "$BIN_DIR/$1" 10 | -------------------------------------------------------------------------------- /examples/run/stdlib.dpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void main() { 5 | printf("Hello world\n".ptr); 6 | 7 | enum numInts = 4; 8 | auto ints = cast(int*) malloc(int.sizeof * numInts); 9 | scope(exit) free(ints); 10 | 11 | foreach(int i; 0 .. numInts) { 12 | ints[i] = i; 13 | printf("ints[%d]: %d ".ptr, i, ints[i]); 14 | } 15 | 16 | printf("\n".ptr); 17 | } 18 | -------------------------------------------------------------------------------- /features/defines.feature: -------------------------------------------------------------------------------- 1 | Feature: Passing macros on the command-line 2 | As a D programmer 3 | I want to pass preprocessor macros on the command-line 4 | So I can select compile-time switches in #included headers 5 | 6 | Scenario: Empty definition 7 | Given a file named "foo.h" with: 8 | """ 9 | #ifdef FOO 10 | struct Foo { int i; }; 11 | #else 12 | struct Bar { int j; }; 13 | #endif 14 | """ 15 | And a file named "foo.dpp" with: 16 | """ 17 | #include "foo.h" 18 | void main() { 19 | Foo f; 20 | f.i = 42; 21 | } 22 | """ 23 | 24 | Then I successfully run `d++ --keep-pre-cpp-files --keep-d-files --define FOO foo.dpp` 25 | 26 | 27 | Scenario: Definition with value 28 | Given a file named "foo.h" with: 29 | """ 30 | #if FOO == 42 31 | struct Foo { int i; }; 32 | #else 33 | struct Bar { int j; }; 34 | #endif 35 | """ 36 | And a file named "foo.dpp" with: 37 | """ 38 | #include "foo.h" 39 | void main() { 40 | Foo f; 41 | f.i = 42; 42 | } 43 | """ 44 | 45 | Then I successfully run `d++ --keep-pre-cpp-files --keep-d-files --define FOO=42 foo.dpp` 46 | -------------------------------------------------------------------------------- /features/multiple_files.feature: -------------------------------------------------------------------------------- 1 | Feature: Compiling multiple .dpp files 2 | As a D programmer 3 | I want to compile several .dpp files at once 4 | So I don't have to compile them one at a time 5 | 6 | @wip 7 | Scenario: 3 .dpp files 8 | 9 | Given a file named "foo.h" with: 10 | """ 11 | #ifndef FOO_H 12 | #define FOO_H 13 | struct Foo { 14 | int i; 15 | }; 16 | struct Foo addFoos(struct Foo* foo1, struct Foo* foo2); 17 | #endif 18 | """ 19 | 20 | And a file named "defs.h" with: 21 | """ 22 | #define FACTOR 2 23 | """ 24 | And a file named "foo.c" with: 25 | """ 26 | #include "foo.h" 27 | struct Foo addFoos(struct Foo* foo1, struct Foo* foo2) { 28 | struct Foo foo; 29 | foo.i = foo1->i + foo2->i; 30 | return foo; 31 | } 32 | """ 33 | 34 | And a file named "foo.dpp" with: 35 | """ 36 | #include "foo.h" 37 | """ 38 | 39 | And a file named "io.dpp" with: 40 | """ 41 | #include 42 | import foo; 43 | void printFoo(Foo foo) { 44 | import std.conv: to; 45 | import std.string: toStringz; 46 | printf("%s\n", foo.to!string.toStringz); 47 | } 48 | """ 49 | 50 | And a file named "maths.dpp" with: 51 | """ 52 | #include "defs.h" 53 | import foo; 54 | Foo addFoosMul2(Foo foo1, Foo foo2) { 55 | auto ret = addFoos(&foo1, &foo2); 56 | ret.i *= FACTOR; 57 | return ret; 58 | } 59 | """ 60 | 61 | And a file named "app.d" with: 62 | """ 63 | import foo; 64 | import io; 65 | import maths; 66 | import std.conv: to; 67 | void main(string[] args) { 68 | auto f1 = Foo(args[1].to!int); 69 | auto f2 = Foo(args[2].to!int); 70 | auto r = addFoosMul2(f1, f2); 71 | printFoo(r); 72 | } 73 | """ 74 | 75 | When I successfully run `gcc -o c.o -c foo.c` 76 | And I successfully run `d++ --keep-d-files app.d foo.dpp io.dpp maths.dpp c.o` 77 | When I successfully run `./app 3 4` 78 | Then the output should contain: 79 | """ 80 | Foo(14) 81 | """ 82 | When I successfully run `./app 5 8` 83 | Then the output should contain: 84 | """ 85 | Foo(26) 86 | """ 87 | -------------------------------------------------------------------------------- /features/preprocess.feature: -------------------------------------------------------------------------------- 1 | Feature: Generating D translations 2 | As a D programmer 3 | I want to only preprocess dpp files 4 | So I can have a D translation of the #included header files 5 | 6 | Scenario: Empty definition 7 | Given a file named "foo.h" with: 8 | """ 9 | int inc(int); 10 | """ 11 | Given a file named "bar.h" with: 12 | """ 13 | int add(int, int); 14 | """ 15 | Given a file named "baz.h" with: 16 | """ 17 | """ 18 | And a file named "foo.dpp" with: 19 | """ 20 | #include "foo.h" 21 | """ 22 | And a file named "bar.dpp" with: 23 | """ 24 | #include "bar.h" 25 | """ 26 | And a file named "baz.dpp" with: 27 | """ 28 | #include "baz.h" 29 | """ 30 | 31 | When I successfully run `d++ --preprocess-only foo.dpp bar.dpp bar.dpp` 32 | Then a file named "foo.d" should exist 33 | And a file named "bar.d" should exist 34 | -------------------------------------------------------------------------------- /features/simple_c_header.feature: -------------------------------------------------------------------------------- 1 | Feature: Compiling a .dpp file that includes a simple C header 2 | As a D programmer 3 | I want to compile a .dpp file including a C header in my program 4 | So I can call legacy code 5 | 6 | Background: 7 | Given a file named "foo.h" with: 8 | """ 9 | #ifndef FOO_H 10 | #define FOO_H 11 | struct Foo { 12 | int i; 13 | }; 14 | struct Foo addFoos(struct Foo* foo1, struct Foo* foo2); 15 | #endif 16 | """ 17 | 18 | And a file named "foo.c" with: 19 | """ 20 | #include "foo.h" 21 | struct Foo addFoos(struct Foo* foo1, struct Foo* foo2) { 22 | struct Foo foo; 23 | foo.i = foo1->i + foo2->i; 24 | return foo; 25 | } 26 | """ 27 | 28 | And a file named "main.dpp" with: 29 | """ 30 | #include "foo.h" 31 | void main(string[] args) { 32 | import std.stdio; 33 | import std.conv; 34 | 35 | auto foo1 = Foo(5); 36 | auto foo2 = Foo(7); 37 | assert(addFoos(&foo1, &foo2) == Foo(12)); 38 | 39 | foo1 = Foo(args[1].to!int); 40 | foo2 = Foo(args[2].to!int); 41 | 42 | writeln(`Foo(`, args[1], `) + Foo(`, args[2], `) = `, addFoos(&foo1, &foo2)); 43 | } 44 | """ 45 | 46 | Scenario: A C header with a struct and a function 47 | 48 | When I successfully run `gcc -o foo.o -c foo.c` 49 | And I successfully run `d++ -ofapp main.dpp foo.o` 50 | When I successfully run `./app 3 4` 51 | Then the output should contain: 52 | """ 53 | Foo(3) + Foo(4) = Foo(7) 54 | """ 55 | And a file named "main.d" should not exist 56 | 57 | 58 | Scenario: A C header with a struct and a function and no -of option 59 | 60 | When I successfully run `gcc -o foo.o -c foo.c` 61 | And I successfully run `d++ main.dpp foo.o` 62 | When I successfully run `./main 3 4` 63 | Then the output should contain: 64 | """ 65 | Foo(3) + Foo(4) = Foo(7) 66 | """ 67 | And a file named "main.d" should not exist 68 | 69 | 70 | Scenario: Only preprocess the file 71 | When I successfully run `gcc -o foo.o -c foo.c` 72 | And I successfully run `d++ --preprocess-only main.dpp` 73 | And I successfully run `dmd -ofapp main.d foo.o` 74 | When I successfully run `./app 3 4` 75 | Then the output should contain: 76 | """ 77 | Foo(3) + Foo(4) = Foo(7) 78 | """ 79 | 80 | Scenario: Compile but don't link 81 | When I successfully run `d++ -c main.dpp` 82 | Then a file named "main.o" should exist 83 | -------------------------------------------------------------------------------- /features/simple_cpp_header.feature: -------------------------------------------------------------------------------- 1 | Feature: Preprocessing a .dpp file that includes a simple C++ header 2 | As a D programmer 3 | I want to compile a .dpp file including a C++ header in my program 4 | So I can call legacy code 5 | 6 | Background: 7 | Given a file named "foo.hpp" with: 8 | """ 9 | #ifndef FOO_HPP 10 | #define FOO_HPP 11 | struct Foo { 12 | int i; 13 | }; 14 | struct Foo addFoos(struct Foo* foo1, struct Foo* foo2); 15 | #endif 16 | """ 17 | 18 | And a file named "foo.cpp" with: 19 | """ 20 | #include "foo.hpp" 21 | Foo addFoos(Foo* foo1, Foo* foo2) { 22 | return { foo1->i + foo2-> i}; 23 | } 24 | """ 25 | 26 | And a file named "main.dpp" with: 27 | """ 28 | #include "foo.hpp" 29 | void main(string[] args) { 30 | import std.stdio; 31 | import std.conv; 32 | 33 | auto foo1 = Foo(5); 34 | auto foo2 = Foo(7); 35 | assert(addFoos(&foo1, &foo2) == Foo(12)); 36 | 37 | foo1 = Foo(args[1].to!int); 38 | foo2 = Foo(args[2].to!int); 39 | 40 | writeln(`Foo(`, args[1], `) + Foo(`, args[2], `) = `, addFoos(&foo1, &foo2)); 41 | } 42 | """ 43 | 44 | Scenario: A C header with a struct and a function 45 | When I successfully run `g++ -o foo.o -c foo.cpp` 46 | And I successfully run `d++ -ofapp main.dpp foo.o` 47 | When I successfully run `./app 3 4` 48 | Then the output should contain: 49 | """ 50 | Foo(3) + Foo(4) = Foo(7) 51 | """ 52 | 53 | Scenario: A C header with a struct and a function and no -of option 54 | When I successfully run `g++ -o foo.o -c foo.cpp` 55 | And I successfully run `d++ main.dpp foo.o` 56 | When I successfully run `./main 3 4` 57 | Then the output should contain: 58 | """ 59 | Foo(3) + Foo(4) = Foo(7) 60 | """ 61 | 62 | Scenario: Preprocess only 63 | When I successfully run `g++ -o foo.o -c foo.cpp` 64 | And I successfully run `d++ --preprocess-only main.dpp` 65 | And I successfully run `dmd -ofapp main.d foo.o` 66 | When I successfully run `./app 3 4` 67 | Then the output should contain: 68 | """ 69 | Foo(3) + Foo(4) = Foo(7) 70 | """ 71 | -------------------------------------------------------------------------------- /features/support/env.rb: -------------------------------------------------------------------------------- 1 | require 'aruba/cucumber' 2 | World(Aruba::Api) 3 | -------------------------------------------------------------------------------- /reggaefile.d: -------------------------------------------------------------------------------- 1 | import reggae; 2 | import std.array: join; 3 | import std.typecons: Yes, No; 4 | 5 | enum debugFlags = ["-w", "-g", "-debug"]; 6 | enum releaseFlags = ["-O", "-inline", "-release"]; 7 | 8 | alias exe = dubDefaultTarget!(CompilerFlags(releaseFlags)); 9 | 10 | alias lib = dubConfigurationTarget!( 11 | Configuration("library"), 12 | CompilerFlags(debugFlags), 13 | LinkerFlags(), 14 | CompilationMode.package_, 15 | ); 16 | 17 | enum mainObj = objectFile( 18 | SourceFile("source/main.d"), 19 | Flags(debugFlags), 20 | ImportPaths("source") 21 | ); 22 | 23 | alias utOld = dubTestTarget!( 24 | CompilerFlags(debugFlags), 25 | LinkerFlags(), 26 | ); 27 | 28 | // alias ut = dubLink!( 29 | // TargetName("ut"), 30 | // Configuration("unittest"), 31 | // targetConcat!(lib, testObjs, dubDependencies!(Configuration("unittest"))), 32 | // ); 33 | alias all_tests = dubTestTarget!( 34 | CompilerFlags(debugFlags), 35 | ); 36 | 37 | alias dpp2 = dubTarget!( 38 | TargetName("dpp2"), 39 | Configuration("dpp2"), 40 | CompilerFlags(debugFlags ~ "-unittest"), 41 | LinkerFlags(), 42 | CompilationMode.all, 43 | ); 44 | 45 | // unitThreadedLight, compiles the whole project per D package 46 | alias utlPerPackage = dubTarget!(TargetName("utl_per_package"), 47 | Configuration("unittest"), 48 | CompilerFlags(debugFlags ~ "-version=unitThreadedLight"), 49 | ); 50 | 51 | 52 | // The rest of this file is just to set up a custom unit test build 53 | // that compiles the production code per package and the test code 54 | // per module. 55 | 56 | // The test code object files 57 | // We build the default configuration to avoid depencencies 58 | // or -unittest. 59 | alias testObjsLight = dlangObjectsPerModule!( 60 | Sources!"tests", 61 | CompilerFlags(debugFlags ~ ["-unittest", "-version=unitThreadedLight"]), 62 | dubImportPaths!(Configuration("unittest")) 63 | ); 64 | 65 | 66 | alias testObjs = dlangObjectsPerModule!( 67 | Sources!"tests", 68 | CompilerFlags(debugFlags ~ ["-unittest"]), 69 | dubImportPaths!(Configuration("unittest")) 70 | ); 71 | 72 | 73 | 74 | // The object file(s) for unit-threaded. 75 | // `dubDependencies` could give us this, but it'd include libclang and any other 76 | // dependencies compiled with `-unittest`, which we'd rather avoid. 77 | alias unitThreadedLight = dubPackageObjects!( 78 | DubPackageName("unit-threaded"), 79 | Configuration("unittest"), // or else the dependency doesn't even exist 80 | CompilerFlags(["-unittest", "-version=unitThreadedLight"]), 81 | ); 82 | 83 | 84 | alias utl = dubLink!( 85 | TargetName("utl"), 86 | Configuration("unittest"), 87 | targetConcat!(lib, testObjsLight, unitThreadedLight), 88 | LinkerFlags("-main"), 89 | ); 90 | 91 | 92 | alias it = dubConfigurationTarget!( 93 | Configuration("integration"), 94 | CompilerFlags(debugFlags ~ "-unittest"), 95 | ); 96 | 97 | 98 | alias ct = dubConfigurationTarget!( 99 | Configuration("contract"), 100 | CompilerFlags(debugFlags ~ "-unittest"), 101 | ); 102 | 103 | 104 | mixin build!( 105 | exe, 106 | optional!all_tests, 107 | optional!it, 108 | optional!ct, 109 | optional!utl, // fast development 110 | optional!utlPerPackage, // for benchmarking 111 | optional!dpp2, 112 | ); 113 | -------------------------------------------------------------------------------- /source/dpp/clang/package.d: -------------------------------------------------------------------------------- 1 | /** 2 | libclang utility code 3 | */ 4 | module dpp.clang; 5 | 6 | 7 | import dpp.from; 8 | 9 | 10 | from!"clang".Cursor namespace(in from!"clang".Cursor cursor) @safe { 11 | import clang: Cursor; 12 | 13 | auto ret = Cursor(cursor.cx); 14 | 15 | while(!ret.isInvalid && ret.kind != Cursor.Kind.Namespace) 16 | ret = ret.semanticParent; 17 | 18 | return ret; 19 | } 20 | 21 | 22 | /** 23 | Returns the type name without namespaces. 24 | */ 25 | string typeNameNoNs(in from!"clang".Cursor cursor) @safe { 26 | import clang: Cursor; 27 | import std.array: join; 28 | import std.algorithm: reverse; 29 | 30 | string[] parts; 31 | 32 | auto c = Cursor(cursor.cx); 33 | 34 | bool isWanted(in Cursor c) { 35 | return 36 | !c.isInvalid 37 | && c.kind != Cursor.Kind.Namespace 38 | && c.kind != Cursor.Kind.TranslationUnit 39 | ; 40 | } 41 | 42 | while(isWanted(c.semanticParent)) { 43 | c = c.semanticParent; 44 | parts ~= c.spelling.idup; 45 | } 46 | 47 | parts = parts.reverse ~ cursor.spelling; 48 | 49 | return parts.join("::"); 50 | } 51 | 52 | 53 | /** 54 | If the cursor is a virtual function that overrides 55 | another virtual function. 56 | */ 57 | bool isOverride(in from!"clang".Cursor cursor) @safe { 58 | import clang: Cursor; 59 | import std.algorithm: any, map, filter, joiner; 60 | 61 | bool hasOverrideAttr(in Cursor cursor) { 62 | return cursor 63 | .children 64 | .any!(a => a.kind == Cursor.Kind.CXXOverrideAttr) 65 | ; 66 | } 67 | 68 | if(hasOverrideAttr(cursor)) return true; 69 | 70 | auto parents = baseClasses(cursor.semanticParent); 71 | const virtualWithSameName = parents 72 | .map!(a => a.children) 73 | .joiner 74 | .filter!(a => a.spelling == cursor.spelling) 75 | .any!(a => a.isVirtual) 76 | ; 77 | 78 | return virtualWithSameName; 79 | } 80 | 81 | 82 | /** 83 | If the cursor is a `final` member function. 84 | */ 85 | bool isFinal(in from!"clang".Cursor cursor) @safe { 86 | import clang: Cursor; 87 | import std.algorithm: any; 88 | 89 | return cursor 90 | .children 91 | .any!(a => a.kind == Cursor.Kind.CXXFinalAttr) 92 | ; 93 | } 94 | 95 | 96 | /** 97 | All base classes this cursor derives from 98 | */ 99 | from!"clang".Cursor[] baseClasses(in from!"clang".Cursor cursor) @safe { 100 | import clang: Cursor; 101 | import std.algorithm: map, filter, joiner; 102 | import std.array: array; 103 | import std.range: chain; 104 | 105 | auto baseSpecifiers = cursor 106 | .children 107 | .filter!(a => a.kind == Cursor.Kind.CXXBaseSpecifier); 108 | if(baseSpecifiers.empty) return []; 109 | 110 | auto baseCursors = baseSpecifiers.map!(a => a.children[0].referencedCursor); 111 | return chain( 112 | baseCursors, 113 | baseCursors.map!baseClasses.joiner, 114 | ).array; 115 | } 116 | 117 | bool hasAnonymousSpelling(in string spelling) @safe pure nothrow { 118 | import std.algorithm : canFind; 119 | return spelling.canFind("(anonymous") || spelling.canFind("(unnamed"); 120 | } 121 | 122 | bool isSortaAnonymous(in from!"clang".Cursor cursor) @safe pure nothrow { 123 | return cursor.spelling == "" || cursor.isAnonymous; 124 | } 125 | -------------------------------------------------------------------------------- /source/dpp/from.d: -------------------------------------------------------------------------------- 1 | /** 2 | Utility to avoid top-level imports 3 | */ 4 | module dpp.from; 5 | 6 | /** 7 | Local imports everywhere. 8 | */ 9 | template from(string moduleName) { 10 | mixin("import from = " ~ moduleName ~ ";"); 11 | } 12 | -------------------------------------------------------------------------------- /source/dpp/runtime/package.d: -------------------------------------------------------------------------------- 1 | module dpp.runtime; 2 | 3 | public import dpp.runtime.app; 4 | public import dpp.runtime.options; 5 | -------------------------------------------------------------------------------- /source/dpp/translation/dlang.d: -------------------------------------------------------------------------------- 1 | /** 2 | Deals with D-specific translation, such as avoiding keywords 3 | */ 4 | module dpp.translation.dlang; 5 | 6 | import dpp.from; 7 | 8 | 9 | string maybeRename(in from!"clang".Cursor cursor, 10 | in from!"dpp.runtime.context".Context context) 11 | @safe pure nothrow 12 | { 13 | return nameClashes(cursor, context) 14 | ? rename(cursor.spelling, context) 15 | : cursor.spelling.idup; 16 | } 17 | 18 | string maybePragma(in from!"clang".Cursor cursor, 19 | in from!"dpp.runtime.context".Context context) 20 | @safe pure nothrow 21 | { 22 | import dpp.runtime.context: Language; 23 | import std.algorithm: startsWith; 24 | const isCpp = context.language == Language.Cpp; 25 | if(isCpp) return pragmaMangle(cursor.mangling); 26 | return nameClashes(cursor, context) ? pragmaMangle(cursor.mangling) : ""; 27 | } 28 | 29 | string rename(string spelling, 30 | in from!"dpp.runtime.context".Context context) 31 | @safe pure nothrow 32 | { 33 | do 34 | spelling ~= "_"; 35 | while(spelling in context.aggregateDeclarations || isKeyword(spelling)); 36 | 37 | return spelling; 38 | } 39 | 40 | string pragmaMangle(in string mangling) @safe pure nothrow { 41 | return mangling == "" ? "" : `pragma(mangle, "` ~ mangling ~ `") `; 42 | } 43 | 44 | private bool nameClashes(in from!"clang".Cursor cursor, 45 | in from!"dpp.runtime.context".Context context) 46 | @safe pure nothrow 47 | { 48 | return 49 | cursor.spelling.isKeyword || 50 | cursor.spelling in context.aggregateDeclarations; 51 | } 52 | 53 | bool isKeyword(string str) @safe @nogc pure nothrow { 54 | switch (str) { 55 | default: return false; 56 | case "abstract": 57 | case "alias": 58 | case "align": 59 | case "asm": 60 | case "assert": 61 | case "auto": 62 | 63 | case "body": 64 | case "bool": 65 | case "break": 66 | case "byte": 67 | 68 | case "case": 69 | case "cast": 70 | case "catch": 71 | case "cdouble": 72 | case "cent": 73 | case "cfloat": 74 | case "char": 75 | case "class": 76 | case "const": 77 | case "continue": 78 | case "creal": 79 | 80 | case "dchar": 81 | case "debug": 82 | case "default": 83 | case "delegate": 84 | case "delete": 85 | case "deprecated": 86 | case "do": 87 | case "double": 88 | 89 | case "else": 90 | case "enum": 91 | case "export": 92 | case "extern": 93 | 94 | case "false": 95 | case "final": 96 | case "finally": 97 | case "float": 98 | case "for": 99 | case "foreach": 100 | case "foreach_reverse": 101 | case "function": 102 | 103 | case "goto": 104 | 105 | case "idouble": 106 | case "if": 107 | case "ifloat": 108 | case "import": 109 | case "in": 110 | case "inout": 111 | case "int": 112 | case "interface": 113 | case "invariant": 114 | case "ireal": 115 | case "is": 116 | 117 | case "lazy": 118 | case "long": 119 | 120 | case "macro": 121 | case "mixin": 122 | case "module": 123 | 124 | case "new": 125 | case "nothrow": 126 | case "null": 127 | 128 | case "out": 129 | case "override": 130 | 131 | case "package": 132 | case "pragma": 133 | case "private": 134 | case "protected": 135 | case "public": 136 | case "pure": 137 | 138 | case "real": 139 | case "ref": 140 | case "return": 141 | 142 | case "scope": 143 | case "shared": 144 | case "short": 145 | case "static": 146 | case "struct": 147 | case "super": 148 | case "switch": 149 | case "synchronized": 150 | 151 | case "template": 152 | case "this": 153 | case "throw": 154 | case "true": 155 | case "try": 156 | case "typedef": 157 | case "typeid": 158 | case "typeof": 159 | 160 | case "ubyte": 161 | case "ucent": 162 | case "uint": 163 | case "ulong": 164 | case "union": 165 | case "unittest": 166 | case "ushort": 167 | 168 | case "version": 169 | case "void": 170 | case "volatile": 171 | 172 | case "wchar": 173 | case "while": 174 | case "with": 175 | case "immutable": 176 | case "__gshared": 177 | case "__thread": 178 | case "__traits": 179 | 180 | case "__EOF__": 181 | case "__FILE__": 182 | case "__LINE__": 183 | case "__DATE__": 184 | case "__TIME__": 185 | case "__TIMESTAMP__": 186 | case "__VENDOR__": 187 | case "__VERSION__": 188 | 189 | case "string": 190 | return true; 191 | } 192 | 193 | assert(0); 194 | } 195 | -------------------------------------------------------------------------------- /source/dpp/translation/docs.d: -------------------------------------------------------------------------------- 1 | module dpp.translation.docs; 2 | 3 | import dpp.from; 4 | 5 | /** Get the attached comments at the location of the cursor */ 6 | string getComment(in from!"clang".Cursor cursor) @safe { 7 | import std.typecons: Nullable; 8 | string docStr = ""; 9 | Nullable!string nullableComment = cursor.raw_comment(); 10 | if(!nullableComment.isNull) { 11 | docStr = nullableComment.get(); 12 | } 13 | return docStr; 14 | } 15 | -------------------------------------------------------------------------------- /source/dpp/translation/enum_.d: -------------------------------------------------------------------------------- 1 | /** 2 | Enum translation 3 | */ 4 | module dpp.translation.enum_; 5 | 6 | import dpp.from; 7 | import dpp.translation.docs; 8 | 9 | string[] translateEnumConstant(in from!"clang".Cursor cursor, 10 | ref from!"dpp.runtime.context".Context context) 11 | @safe 12 | { 13 | import dpp.translation.dlang: maybeRename; 14 | import clang: Cursor; 15 | import std.conv: text; 16 | 17 | assert(cursor.kind == Cursor.Kind.EnumConstantDecl); 18 | context.log(" Enum Constant Value: ", cursor.enumConstantValue); 19 | context.log(" tokens: ", cursor.tokens); 20 | 21 | return [getComment(cursor), maybeRename(cursor, context) ~ ` = ` ~ text(cursor.enumConstantValue) ~ `, `]; 22 | } 23 | -------------------------------------------------------------------------------- /source/dpp/translation/exception.d: -------------------------------------------------------------------------------- 1 | module dpp.translation.exception; 2 | 3 | 4 | /** 5 | Some C++ concepts simply have no D equivalent 6 | */ 7 | class UntranslatableException: Exception { 8 | import std.exception: basicExceptionCtors; 9 | 10 | mixin basicExceptionCtors; 11 | } 12 | -------------------------------------------------------------------------------- /source/dpp/translation/namespace.d: -------------------------------------------------------------------------------- 1 | module dpp.translation.namespace; 2 | 3 | 4 | import dpp.from; 5 | 6 | 7 | string[] translateNamespace(in from!"clang".Cursor cursor, 8 | ref from!"dpp.runtime.context".Context context) 9 | @safe 10 | in(cursor.kind == from!"clang".Cursor.Kind.Namespace) 11 | do 12 | { 13 | import dpp.translation.translation: translate; 14 | import dpp.translation.exception: UntranslatableException; 15 | import clang: Cursor; 16 | import std.conv: text; 17 | import std.algorithm: map, startsWith; 18 | import std.array: array; 19 | 20 | if(shouldSkip(cursor.spelling, context)) 21 | return []; 22 | 23 | if(cursor.spelling == "") 24 | throw new UntranslatableException("BUG: namespace with no name"); 25 | 26 | string[] lines; 27 | 28 | lines ~= [ 29 | `extern(C++, "` ~ cursor.spelling ~ `")`, 30 | `{`, 31 | ]; 32 | 33 | context.pushNamespace(cursor.spelling); 34 | scope(exit) context.popNamespace(cursor.spelling); 35 | 36 | foreach(child; cursor.children) { 37 | 38 | if(child.kind == Cursor.Kind.VisibilityAttr) continue; 39 | if(cursor.spelling == "std" && child.spelling.startsWith("__")) continue; 40 | 41 | lines ~= translate(child, context) 42 | .map!(a => (child.kind == Cursor.Kind.Namespace ? " " : " ") ~ a) 43 | .array; 44 | } 45 | 46 | lines ~= `}`; 47 | 48 | return lines; 49 | } 50 | 51 | 52 | private bool shouldSkip(in string spelling, in from!"dpp.runtime.context".Context context) 53 | @safe pure 54 | { 55 | import std.algorithm: canFind; 56 | return context.options.ignoredNamespaces.canFind(spelling); 57 | } 58 | -------------------------------------------------------------------------------- /source/dpp/translation/package.d: -------------------------------------------------------------------------------- 1 | /** 2 | This module expands each header encountered in the original input file. 3 | It usually delegates to dstep but not always. Since dstep has a different 4 | goal, which is to produce human-readable D files from a header, we can't 5 | just call into it. 6 | 7 | The translate function here will handle the cases it knows how to deal with, 8 | otherwise it asks dstep to it for us. 9 | */ 10 | 11 | module dpp.translation; 12 | 13 | public import dpp.translation.aggregate; 14 | public import dpp.translation.function_; 15 | public import dpp.translation.typedef_; 16 | public import dpp.translation.macro_; 17 | public import dpp.translation.enum_; 18 | public import dpp.translation.variable; 19 | public import dpp.translation.namespace; 20 | public import dpp.translation.template_; 21 | -------------------------------------------------------------------------------- /source/dpp/translation/tokens.d: -------------------------------------------------------------------------------- 1 | module dpp.translation.tokens; 2 | 3 | 4 | import dpp.from; 5 | 6 | 7 | string translateTokens(in from!"clang".Token[] tokens) @safe { 8 | import dpp.translation.type: translateString; 9 | import dpp.runtime.context: Context; 10 | import clang: Token; 11 | import std.algorithm: map, filter, canFind, endsWith; 12 | import std.array: array, join, replace; 13 | 14 | const translatedPropertyTokens = tokens 15 | .translateProperty("sizeof") 16 | .translateProperty("alignof") 17 | ; 18 | 19 | // we can't rely on `translateString` for angle brackets here since it 20 | // checks if there are matching pairs in the string to translate. 21 | // Since we have an array of tokens, the matching pair might be in a different position 22 | 23 | const canFindOpeningAngle = translatedPropertyTokens 24 | .canFind!(t => t.kind == Token.Kind.Punctuation && t.spelling == "<"); 25 | const canFindClosingAngle = translatedPropertyTokens 26 | .canFind!(t => t.kind == Token.Kind.Punctuation && (t.spelling == ">" || t.spelling == ">>")); 27 | 28 | const translatedAngleBracketTokens = canFindOpeningAngle && canFindClosingAngle 29 | ? translatedPropertyTokens 30 | .map!(t => Token(t.kind, t.spelling.replace("<", "!(").replace(">>", "))").replace(">", ")"))) 31 | .array 32 | : translatedPropertyTokens; 33 | 34 | auto ret = translatedAngleBracketTokens 35 | .filter!(t => t.kind != Token.Kind.Keyword || t.spelling != "typename") 36 | .map!(t => translateString(t.spelling, Context())) 37 | .join; 38 | 39 | // this can happen because of ending with ">>" 40 | if(ret.endsWith("))")) ret = ret[0 .. $-1]; 41 | 42 | return ret; 43 | } 44 | 45 | 46 | // sizeof(foo) -> foo.sizeof, alignof(foo) -> foo.alignof 47 | private auto translateProperty(const(from!"clang".Token)[] tokens, in string property) @safe { 48 | import dpp.translation.type: translateString; 49 | import dpp.runtime.context: Context; 50 | import clang: Token; 51 | import std.algorithm: countUntil, map; 52 | import std.array: join, replace; 53 | 54 | for(;;) { 55 | const indexProperty = tokens.countUntil!(a => a.kind == Token.Kind.Keyword && a.spelling == property); 56 | if(indexProperty == -1) return tokens; 57 | const indexCloseParen = indexProperty + tokens[indexProperty..$].countUntil!(a => a.kind == Token.Kind.Punctuation && a.spelling == ")"); 58 | const newTokenSpelling = 59 | "(" ~ tokens[indexProperty + 2 .. indexCloseParen] 60 | .map!(a => a.spelling) 61 | .join(" ") 62 | ~ ")." ~ property 63 | ; 64 | 65 | tokens = 66 | tokens[0 .. indexProperty] ~ 67 | Token(Token.Kind.Literal, translateString(newTokenSpelling, Context())) ~ 68 | tokens[indexCloseParen + 1 .. $]; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /source/dpp/translation/typedef_.d: -------------------------------------------------------------------------------- 1 | /** 2 | typedef translations 3 | */ 4 | module dpp.translation.typedef_; 5 | 6 | 7 | import dpp.from; 8 | import dpp.translation.docs; 9 | 10 | 11 | string[] translateTypedef(in from!"clang".Cursor cursor, 12 | ref from!"dpp.runtime.context".Context context) 13 | @safe 14 | { 15 | // canonical because otherwise tests fail on Travis's version of libclang 16 | return isSomeFunction(cursor.underlyingType.canonical) 17 | ? translateFunction(cursor, context.indent) 18 | : translateNonFunction(cursor, context); 19 | } 20 | 21 | 22 | string[] translateNonFunction(in from!"clang".Cursor cursor, 23 | ref from!"dpp.runtime.context".Context context) 24 | @safe 25 | { 26 | import clang: Cursor, Type; 27 | import std.algorithm: filter; 28 | import std.array: array; 29 | 30 | auto childrenRange = cursor 31 | .children 32 | .filter!(a => !a.isInvalid) 33 | // only interested in the actual type we're aliasing 34 | .filter!(a => a.type.kind != Type.Kind.Invalid) 35 | ; 36 | 37 | // who knows why this is @system 38 | const children = () @trusted { return childrenRange.array; }(); 39 | 40 | // children might contain 0, 1, or more entries due to libclang particularities 41 | context.log("Children: ", children); 42 | context.log("Underlying type: ", cursor.underlyingType); 43 | 44 | // If the child is a top-level anonymous struct, it's pointless to alias 45 | // it and give the struct a silly name, instead just define a struct with 46 | // the typedef name instead. e.g. 47 | // `typedef struct { int dummy; } Foo` -> `struct Foo { int dummy; }` 48 | // However, this isn't true for enums since an anonymous enum can be declared 49 | // with no typedef. See #54. 50 | const noName = 51 | isTopLevelAnonymous(children) 52 | && children[0].kind != Cursor.Kind.EnumDecl 53 | ; 54 | 55 | // e.g. `typedef struct { int i; } *StructPtr;` 56 | if(noName && cursor.underlyingType.kind == Type.Kind.Pointer) { 57 | import dpp.translation.translation: translate; 58 | 59 | auto translation = context.aggregateIsRemembered(children[0]) ? [] : translate(children[0], context); 60 | auto regularTranslation = translateRegular(cursor, context, children); 61 | 62 | return translation ~ regularTranslation; 63 | 64 | } 65 | 66 | return noName 67 | ? translateTopLevelAnonymous(children[0], context) 68 | : translateRegular(cursor, context, children); 69 | } 70 | 71 | 72 | private bool isTopLevelAnonymous(in from!"clang".Cursor[] children) 73 | @safe nothrow 74 | { 75 | import dpp.clang: isSortaAnonymous; 76 | import clang: Cursor; 77 | 78 | return 79 | children.length == 1 && // so we can inspect it 80 | children[0].isSortaAnonymous && // anonymous 81 | children[0].lexicalParent.kind == Cursor.Kind.TranslationUnit && // top-level 82 | children[0].kind != Cursor.Kind.ParmDecl // a lot more should be here 83 | ; 84 | } 85 | 86 | // non-anonymous non-function typedef 87 | private string[] translateRegular(in from!"clang".Cursor cursor, 88 | ref from!"dpp.runtime.context".Context context, 89 | in from!"clang".Cursor[] children) 90 | @safe 91 | { 92 | import dpp.translation.type: translate, removeDppDecorators; 93 | import dpp.translation.aggregate: isAggregateC; 94 | import dpp.translation.dlang: maybeRename; 95 | import dpp.clang: isSortaAnonymous; 96 | import clang: Type; 97 | import std.typecons: No; 98 | 99 | auto underlyingSpelling = () { 100 | switch(cursor.spelling) { 101 | default: 102 | // The cursor will have a type with spelling despite not having a spelling itself. 103 | // We use the nickname we've given it in D if it's the case. 104 | const isAnonymousEnum = 105 | children.length == 1 && 106 | isAggregateC(children[0]) && 107 | children[0].isSortaAnonymous && 108 | children[0].type.kind == Type.Kind.Enum 109 | ; 110 | 111 | return isAnonymousEnum 112 | ? context.spellingOrNickname(children[0]) 113 | : translate(cursor.underlyingType, context, No.translatingFunction) 114 | .removeDppDecorators; 115 | 116 | // possible issues on 32-bit 117 | case "int32_t": return "int"; 118 | case "uint32_t": return "uint"; 119 | case "in64_t": return "long"; 120 | case "uint64_t": return "ulong"; 121 | case "nullptr_t": return "typeof(null)"; 122 | } 123 | }(); 124 | 125 | context.rememberType(cursor.spelling); 126 | 127 | context.log(""); 128 | 129 | // This is to prevent trying to translate `typedef struct Struct Struct;` which 130 | // makes no sense in D. 131 | return cursor.spelling == underlyingSpelling 132 | ? [] 133 | : [getComment(cursor), `alias ` ~ maybeRename(cursor, context) ~ ` = ` ~ underlyingSpelling ~ `;`]; 134 | } 135 | 136 | 137 | private string[] translateFunction(in from!"clang".Cursor typedef_, 138 | ref from!"dpp.runtime.context".Context context) 139 | @safe 140 | { 141 | import dpp.translation.type: translate; 142 | import dpp.translation.function_: translateParamTypes; 143 | import clang: Cursor, Type; 144 | import std.algorithm: map, filter; 145 | import std.array: join; 146 | 147 | const underlyingType = typedef_.underlyingType.canonical; 148 | const returnType = underlyingType.kind == Type.Kind.Pointer 149 | ? underlyingType.pointee.returnType 150 | : underlyingType.returnType; 151 | context.log("Function typedef return type: ", returnType); 152 | const returnTypeTransl = translate(returnType, context); 153 | 154 | const functionType = typedef_.underlyingType.canonical.kind == Type.Kind.Pointer 155 | ? typedef_.underlyingType.canonical.pointee 156 | : typedef_.underlyingType.canonical; 157 | 158 | const params = translateParamTypes(typedef_, functionType, context.indent).join(", "); 159 | return [`alias ` ~ typedef_.spelling ~ ` = ` ~ returnTypeTransl ~ ` function(` ~ params ~ `);`]; 160 | 161 | } 162 | 163 | private bool isSomeFunction(in from!"clang".Type type) @safe pure nothrow { 164 | import clang: Type; 165 | 166 | const isFunctionPointer = 167 | type.kind == Type.Kind.Pointer && 168 | type.pointee.kind == Type.Kind.FunctionProto; 169 | const isFunction = type.kind == Type.Kind.FunctionProto; 170 | 171 | return isFunctionPointer || isFunction; 172 | } 173 | 174 | private string[] translateTopLevelAnonymous(in from!"clang".Cursor cursor, 175 | ref from!"dpp.runtime.context".Context context) 176 | @safe 177 | { 178 | import dpp.translation.translation: translate; 179 | import clang: Cursor; 180 | 181 | // the old cursor has no spelling, so construct a new one 182 | auto newCursor = Cursor(cursor.cx); 183 | 184 | // the type spelling will be the name of the struct, union, or enum 185 | newCursor.setSpelling(cursor.type.spelling); 186 | 187 | // delegate to whoever knows what they're doing 188 | return translate(newCursor, context); 189 | } 190 | -------------------------------------------------------------------------------- /source/dpp/translation/variable.d: -------------------------------------------------------------------------------- 1 | module dpp.translation.variable; 2 | 3 | import dpp.from; 4 | import dpp.translation.docs; 5 | 6 | string[] translateVariable(in from!"clang".Cursor cursor, 7 | ref from!"dpp.runtime.context".Context context) 8 | @safe 9 | in(cursor.kind == from!"clang".Cursor.Kind.VarDecl) 10 | do 11 | { 12 | import dpp.translation.exception: UntranslatableException; 13 | import dpp.translation.dlang: maybePragma; 14 | import dpp.translation.translation: translateCursor = translate; 15 | import dpp.translation.type: translateType = translate, hasAnonymousSpelling; 16 | import dpp.translation.tokens: translateTokens; 17 | import clang: Cursor, Type, Token, Linkage; 18 | import std.conv: text; 19 | import std.typecons: No; 20 | import std.algorithm: canFind, find, map; 21 | import std.array: empty, popFront, join, replace; 22 | 23 | string[] ret; 24 | 25 | const isAnonymous = cursor.type.hasAnonymousSpelling; 26 | // If the type is anonymous, then we need to define it before we declare 27 | // ourselves of that type, unless that type is an enum. See #54. 28 | if(isAnonymous && cursor.type.canonical.declaration.kind != Cursor.Kind.EnumDecl) { 29 | ret ~= translateCursor(cursor.type.canonical.declaration, context); 30 | } 31 | 32 | // variables can be declared multiple times in C but only one in D 33 | if(!cursor.isCanonical) return []; 34 | 35 | // Don't bother if we don't have a definition anywhere - C allows this but D 36 | // doesn't. See it.compile.projects.ASN1_ITEM or try #including . 37 | // There will be a problem with the global variables such as DHparams_it that 38 | // have a struct with an unknown type unless one includes 39 | // as well. In C, as long as one doesn't try to do anything with the variable, 40 | // that's ok. In D, it's not. Essentially: 41 | // struct Foo; 42 | // extern Foo gFoo; 43 | if(isRecordWithoutDefinition(cursor, context)) return []; 44 | 45 | const spelling = context.rememberLinkable(cursor); 46 | 47 | // global variable or static member of a struct/class? 48 | const static_ = cursor.semanticParent.type.canonical.kind == Type.Kind.Record 49 | ? "static " 50 | : ""; 51 | // e.g. enum foo = 42; 52 | const constexpr = cursor.tokens.canFind(Token(Token.Kind.Keyword, "constexpr")); 53 | const dllexport = cursor.linkage == Linkage.External 54 | ? "export " : ""; 55 | 56 | if(constexpr) 57 | ret ~= translateConstexpr(spelling, cursor, context); 58 | else { 59 | const maybeTypeSpelling = translateType(cursor.type, context, No.translatingFunction); 60 | // In C it is possible to have an extern void variable 61 | const typeSpelling = cursor.type.kind == Type.Kind.Void 62 | ? maybeTypeSpelling.replace("void", "void*") 63 | : maybeTypeSpelling; 64 | ret ~= 65 | maybePragma(cursor, context) ~ 66 | text("extern ", dllexport, "__gshared ", static_, typeSpelling, " ", spelling, ";"); 67 | } 68 | 69 | // attach variable docs 70 | ret = getComment(cursor) ~ ret; 71 | 72 | return ret; 73 | } 74 | 75 | 76 | private string[] translateConstexpr(in string spelling, 77 | in from!"clang".Cursor cursor, 78 | ref from!"dpp.runtime.context".Context context) 79 | @safe 80 | { 81 | import dpp.translation.tokens: translateTokens; 82 | import dpp.translation.exception: UntranslatableException; 83 | import dpp.translation.type: translate; 84 | import clang: Cursor, Token; 85 | import std.algorithm: find, canFind; 86 | import std.conv: text; 87 | import std.array: empty, popFront; 88 | import std.typecons: No; 89 | 90 | auto tokens = cursor.tokens; 91 | tokens = tokens.find!(a => a.kind == Token.Kind.Punctuation && a.spelling == "="); 92 | 93 | const init = () { 94 | // see contract.constexpr.variable.init.braces 95 | if(cursor.children.canFind!(c => c.kind == Cursor.Kind.InitListExpr)) 96 | return " = " ~ translate(cursor.type, context, No.translatingFunction) ~ ".init"; 97 | 98 | if(!tokens.empty) { 99 | tokens.popFront; 100 | return " = " ~ translateTokens(tokens); 101 | } 102 | 103 | throw new UntranslatableException( 104 | text("Could not find assignment in ", cursor.tokens, 105 | "\ncursor: ", cursor, "\n@ ", cursor.sourceRange)); 106 | }(); 107 | 108 | return [ text("enum ", spelling, init, ";") ]; 109 | } 110 | 111 | private bool isRecordWithoutDefinition( 112 | in from!"clang".Cursor cursor, 113 | ref from!"dpp.runtime.context".Context context) 114 | @safe 115 | { 116 | import clang: Cursor, Type; 117 | 118 | const canonicalType = cursor.type.canonical; 119 | 120 | if(canonicalType.kind != Type.Kind.Record) 121 | return false; 122 | 123 | const declaration = canonicalType.declaration; 124 | const definition = declaration.definition; 125 | const specializedTemplate = declaration.specializedCursorTemplate; 126 | 127 | context.log("canonicalType: ", canonicalType); 128 | context.log("declaration: ", declaration); 129 | context.log("definition: ", definition); 130 | context.log("specialised cursor template: ", specializedTemplate); 131 | 132 | return 133 | definition.isInvalid && 134 | // See #97 135 | specializedTemplate.kind != Cursor.Kind.ClassTemplate 136 | ; 137 | } 138 | -------------------------------------------------------------------------------- /source/main.d: -------------------------------------------------------------------------------- 1 | int main(string[] args) { 2 | 3 | import dpp.runtime: run, Options; 4 | import std.stdio: stderr; 5 | 6 | try { 7 | const options = Options(args); 8 | if(options.earlyExit) return 0; 9 | run(options); 10 | return 0; 11 | } catch(Exception ex) { 12 | stderr.writeln("Error: ", ex.msg); 13 | return 1; 14 | } catch(Throwable t) { 15 | stderr.writeln("Fatal error: ", t); 16 | return 2; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/common/package.d: -------------------------------------------------------------------------------- 1 | module common; 2 | 3 | 4 | void printChildren(T)(auto ref T cursorOrTU) { 5 | import clang: TranslationUnit, Cursor; 6 | import std.traits: Unqual; 7 | 8 | static if(is(Unqual!T == TranslationUnit) || is(Unqual!T == Cursor)) { 9 | 10 | import unit_threaded.io: writelnUt; 11 | import std.algorithm: map; 12 | import std.array: join; 13 | import std.conv: text; 14 | 15 | static if(is(Unqual!T == TranslationUnit)) 16 | const children = cursorOrTU.cursor.children; 17 | else 18 | const children = cursorOrTU.children; 19 | 20 | writelnUt("\n", cursorOrTU, " children:\n[\n", children.map!(a => text(" ", a)).join(",\n")); 21 | writelnUt("]\n"); 22 | } 23 | } 24 | 25 | 26 | void shouldMatch(T, K)(T obj, in K kind, in string spelling, in string file = __FILE__, in size_t line = __LINE__) { 27 | import unit_threaded; 28 | static assert(is(K == T.Kind)); 29 | obj.kind.shouldEqual(kind, file, line); 30 | obj.spelling.shouldEqual(spelling, file, line); 31 | } 32 | -------------------------------------------------------------------------------- /tests/contract/array.d: -------------------------------------------------------------------------------- 1 | module contract.array; 2 | 3 | 4 | import contract; 5 | 6 | 7 | @Tags("contract") 8 | @("int[4]") 9 | @safe unittest { 10 | const tu = parse( 11 | C( 12 | q{ 13 | int arr[4]; 14 | } 15 | ) 16 | ); 17 | 18 | tu.children.length.should == 1; 19 | const cursor = tu.children[0]; 20 | 21 | cursor.kind.should == Cursor.Kind.VarDecl; 22 | cursor.spelling.should == "arr"; 23 | 24 | const type = cursor.type; 25 | type.kind.should == Type.Kind.ConstantArray; 26 | try 27 | type.spelling.should == "int [4]"; 28 | catch(Exception _) 29 | type.spelling.should == "int[4]"; 30 | type.canonical.kind.should == Type.Kind.ConstantArray; 31 | try 32 | type.canonical.spelling.should == "int [4]"; 33 | catch(Exception _) 34 | type.canonical.spelling.should == "int[4]"; 35 | } 36 | 37 | 38 | @Tags("contract") 39 | @("flexible") 40 | @safe unittest { 41 | const tu = parse( 42 | C( 43 | q{ 44 | struct Slice { 45 | int length; 46 | unsigned char arr[]; 47 | }; 48 | } 49 | ) 50 | ); 51 | 52 | tu.children.length.should == 1; 53 | const cursor = tu.children[0]; 54 | const structChildren = cursor.children; 55 | structChildren.length.should == 2; 56 | 57 | structChildren[0].kind.should == Cursor.Kind.FieldDecl; 58 | structChildren[0].spelling.should == "length"; 59 | structChildren[0].type.kind.should == Type.Kind.Int; 60 | structChildren[0].type.spelling.should == "int"; 61 | structChildren[0].type.canonical.kind.should == Type.Kind.Int; 62 | structChildren[0].type.canonical.spelling.should == "int"; 63 | 64 | structChildren[1].kind.should == Cursor.Kind.FieldDecl; 65 | structChildren[1].spelling.should == "arr"; 66 | structChildren[1].type.kind.should == Type.Kind.IncompleteArray; 67 | try 68 | structChildren[1].type.spelling.should == "unsigned char []"; 69 | catch(Exception _) 70 | structChildren[1].type.spelling.should == "unsigned char[]"; 71 | 72 | structChildren[1].type.canonical.kind.should == Type.Kind.IncompleteArray; 73 | try 74 | structChildren[1].type.canonical.spelling.should == "unsigned char []"; 75 | catch(Exception _) 76 | structChildren[1].type.canonical.spelling.should == "unsigned char[]"; 77 | } 78 | -------------------------------------------------------------------------------- /tests/contract/constexpr.d: -------------------------------------------------------------------------------- 1 | module contract.constexpr; 2 | 3 | 4 | import contract; 5 | import clang: Token; 6 | 7 | 8 | @("variable.struct.static") 9 | @safe unittest { 10 | 11 | const tu = parse( 12 | Cpp( 13 | q{ 14 | struct Struct { 15 | static constexpr int constExprValue = 42; 16 | static const int constValue = 33; 17 | static int value; 18 | }; 19 | } 20 | ) 21 | ); 22 | 23 | tu.children.length.shouldEqual(1); 24 | 25 | const struct_ = tu.children[0]; 26 | printChildren(struct_); 27 | 28 | struct_.kind.shouldEqual(Cursor.Kind.StructDecl); 29 | struct_.children.length.shouldEqual(3); 30 | 31 | const constexprVar = struct_.children[0]; printChildren(constexprVar); 32 | constexprVar.kind.shouldEqual(Cursor.Kind.VarDecl); 33 | constexprVar.type.kind.shouldEqual(Type.Kind.Int); 34 | constexprVar.type.spelling.shouldEqual("const int"); 35 | constexprVar.type.isConstQualified.shouldBeTrue; 36 | Token(Token.Kind.Keyword, "constexpr").should.be in constexprVar.tokens; 37 | constexprVar.tokens[$-1].spelling.should == "42"; 38 | constexprVar.children.length.should == 1; 39 | 40 | const constExprIntLit = constexprVar.child(0); 41 | constExprIntLit.shouldMatch(Cursor.Kind.IntegerLiteral, ""); 42 | constExprIntLit.type.shouldMatch(Type.Kind.Int, "int"); 43 | constExprIntLit.tokens.should == [Token(Token.Kind.Literal, "42")]; 44 | 45 | const constVar = struct_.children[1]; printChildren(constVar); 46 | constVar.kind.shouldEqual(Cursor.Kind.VarDecl); 47 | constVar.type.kind.shouldEqual(Type.Kind.Int); 48 | constVar.type.spelling.shouldEqual("const int"); 49 | constVar.type.isConstQualified.shouldBeTrue; 50 | Token(Token.Kind.Keyword, "constexpr").should.not.be in constVar.tokens; 51 | constVar.tokens[$-1].spelling.should == "33"; 52 | constVar.children.length.should == 1; 53 | 54 | const constIntLit = constVar.child(0); 55 | constIntLit.shouldMatch(Cursor.Kind.IntegerLiteral, ""); 56 | constIntLit.type.shouldMatch(Type.Kind.Int, "int"); 57 | constIntLit.tokens.should == [Token(Token.Kind.Literal, "33")]; 58 | 59 | const var = struct_.children[2]; printChildren(var); 60 | var.kind.shouldEqual(Cursor.Kind.VarDecl); 61 | var.type.kind.shouldEqual(Type.Kind.Int); 62 | var.type.spelling.shouldEqual("int"); 63 | var.type.isConstQualified.shouldBeFalse; 64 | Token(Token.Kind.Keyword, "constexpr").should.not.be in var.tokens; 65 | var.children.length.should == 0; 66 | } 67 | 68 | 69 | @("variable.init.braces") 70 | @safe unittest { 71 | 72 | const tu = parse( 73 | Cpp( 74 | q{ 75 | constexpr int var{}; 76 | } 77 | ) 78 | ); 79 | 80 | tu.children.length.should == 1; 81 | const var = tu.child(0); 82 | 83 | var.shouldMatch(Cursor.Kind.VarDecl, "var"); 84 | var.type.shouldMatch(Type.Kind.Int, "const int"); 85 | printChildren(var); 86 | var.children.length.should == 1; 87 | 88 | const initList = var.child(0); 89 | initList.shouldMatch(Cursor.Kind.InitListExpr, ""); 90 | initList.type.shouldMatch(Type.Kind.Int, "int"); 91 | initList.tokens.should == [ 92 | Token(Token.Kind.Punctuation, "{"), 93 | Token(Token.Kind.Punctuation, "}") 94 | ]; 95 | initList.children.length.should == 0; 96 | } 97 | -------------------------------------------------------------------------------- /tests/contract/enums.d: -------------------------------------------------------------------------------- 1 | module contract.enums; 2 | 3 | 4 | import contract; 5 | 6 | 7 | @("legacy") 8 | @safe unittest { 9 | 10 | import clang.c.index; 11 | 12 | const tu = parse( 13 | Cpp( 14 | q{ 15 | enum Enum { 16 | foo, 17 | bar, 18 | baz, 19 | }; 20 | } 21 | ) 22 | ); 23 | 24 | tu.children.length.should == 1; 25 | const enum_ = tu.children[0]; 26 | enum_.shouldMatch(Cursor.Kind.EnumDecl, "Enum"); 27 | printChildren(enum_); 28 | enum_.children.length.should == 3; 29 | 30 | version(Windows) 31 | Type(clang_getEnumDeclIntegerType(enum_.cx)).shouldMatch(Type.Kind.Int, "int"); 32 | else 33 | Type(clang_getEnumDeclIntegerType(enum_.cx)).shouldMatch(Type.Kind.UInt, "unsigned int"); 34 | } 35 | 36 | 37 | @("class.type") 38 | @safe unittest { 39 | 40 | import clang.c.index; 41 | 42 | const tu = parse( 43 | Cpp( 44 | q{ 45 | enum class Enum: unsigned char { 46 | foo, 47 | bar, 48 | baz, 49 | }; 50 | } 51 | ) 52 | ); 53 | 54 | tu.children.length.should == 1; 55 | const enum_ = tu.children[0]; 56 | enum_.shouldMatch(Cursor.Kind.EnumDecl, "Enum"); 57 | printChildren(enum_); 58 | enum_.children.length.should == 3; 59 | 60 | Type(clang_getEnumDeclIntegerType(enum_.cx)).shouldMatch(Type.Kind.UChar, "unsigned char"); 61 | } 62 | -------------------------------------------------------------------------------- /tests/contract/functions.d: -------------------------------------------------------------------------------- 1 | module contract.functions; 2 | 3 | 4 | import contract; 5 | import std.array: array; 6 | 7 | 8 | // See #43 9 | @Tags("contract") 10 | @("functionproto.deref") 11 | @safe unittest { 12 | 13 | const tu = parse( 14 | C( 15 | q{ 16 | int binOp(int (f)(int x, int y), int a, int b); 17 | } 18 | ) 19 | ); 20 | 21 | tu.children.length.should == 1; 22 | 23 | const binOp = tu.child(0); 24 | binOp.shouldMatch(Cursor.Kind.FunctionDecl, "binOp"); 25 | 26 | binOp.type.paramTypes.array.length.should == 3; 27 | const f = binOp.type.paramTypes.array[0]; 28 | // need canonical for old versions of libclang 29 | f.canonical.shouldMatch(Type.Kind.FunctionProto, "int (int, int)"); 30 | writelnUt(f.pointee); 31 | // presumably, not a pointer 32 | f.pointee.isInvalid.should == true; 33 | } 34 | 35 | 36 | @Tags("contract") 37 | @("functionproto.star") 38 | @safe unittest { 39 | 40 | const tu = parse( 41 | C( 42 | q{ 43 | int binOp(int (*f)(int x, int y), int a, int b); 44 | } 45 | ) 46 | ); 47 | 48 | tu.children.length.should == 1; 49 | 50 | const binOp = tu.child(0); 51 | binOp.shouldMatch(Cursor.Kind.FunctionDecl, "binOp"); 52 | 53 | binOp.type.paramTypes.array.length.should == 3; 54 | const f = binOp.type.paramTypes.array[0]; 55 | // Even though the declaration here is effectively the same as the 56 | // one in the test above, it shows up as a pointer 57 | f.shouldMatch(Type.Kind.Pointer, "int (*)(int, int)"); 58 | writelnUt(f.pointee); 59 | // it's a pointer 60 | f.pointee.isInvalid.should == false; 61 | 62 | f.pointee.canonical.shouldMatch(Type.Kind.FunctionProto, "int (int, int)"); 63 | } 64 | 65 | 66 | @Tags("contract") 67 | @("typeof") 68 | @safe unittest { 69 | 70 | const tu = parse( 71 | C( 72 | q{ 73 | struct Foo; 74 | // typeof is a gcc and clang language extension 75 | typeof(struct Foo *) func(); 76 | } 77 | ) 78 | ); 79 | 80 | tu.children.length.should == 2; 81 | 82 | const func = tu.child(1); 83 | func.shouldMatch(Cursor.Kind.FunctionDecl, "func"); 84 | printChildren(func); 85 | func.children.length.should == 1; 86 | 87 | func.returnType.shouldMatch(Type.Kind.Unexposed, "typeof(struct Foo *)"); 88 | func.returnType.canonical.shouldMatch(Type.Kind.Pointer, "struct Foo *"); 89 | func.returnType.canonical.pointee.shouldMatch(Type.Kind.Record, "struct Foo"); 90 | } 91 | -------------------------------------------------------------------------------- /tests/contract/issues.d: -------------------------------------------------------------------------------- 1 | module contract.issues; 2 | 3 | 4 | import contract; 5 | 6 | 7 | 8 | @Tags("contract") 9 | @("119") 10 | @safe unittest { 11 | 12 | import clang: Token; 13 | 14 | const tu = parse( 15 | Cpp( 16 | q{ 17 | enum class Enum { foo, bar, baz }; 18 | } 19 | ) 20 | ); 21 | 22 | tu.children.length.should == 1; 23 | const enum_ = tu.child(0); 24 | 25 | enum_.shouldMatch(Cursor.Kind.EnumDecl, "Enum"); 26 | enum_.type.shouldMatch(Type.Kind.Enum, "Enum"); 27 | printChildren(enum_); 28 | enum_.children.length.should == 3; // EnumConstantDecl 29 | 30 | Token(Token.Kind.Keyword, "class").should.be in enum_.tokens; 31 | } 32 | 33 | 34 | 35 | @Tags("contract") 36 | @("126") 37 | @safe unittest { 38 | 39 | import clang.c.index; 40 | 41 | const tu = parse( 42 | Cpp( 43 | q{ 44 | template 45 | struct Foo { 46 | T ts[42]; 47 | }; 48 | } 49 | ) 50 | ); 51 | 52 | tu.children.length.should == 1; 53 | 54 | const foo = tu.child(0); 55 | foo.shouldMatch(Cursor.Kind.ClassTemplate, "Foo"); 56 | foo.type.shouldMatch(Type.Kind.Invalid, ""); 57 | printChildren(foo); 58 | foo.children.length.should == 2; 59 | 60 | const typeParam = foo.child(0); 61 | typeParam.shouldMatch(Cursor.Kind.TemplateTypeParameter, "T"); 62 | typeParam.type.shouldMatch(Type.Kind.Unexposed, "T"); 63 | printChildren(typeParam); 64 | typeParam.children.length.should == 0; 65 | 66 | const fieldDecl = foo.child(1); 67 | fieldDecl.shouldMatch(Cursor.Kind.FieldDecl, "ts"); 68 | try 69 | fieldDecl.type.shouldMatch(Type.Kind.ConstantArray, "T [42]"); 70 | catch(Exception _) 71 | fieldDecl.type.shouldMatch(Type.Kind.ConstantArray, "T[42]"); 72 | 73 | printChildren(fieldDecl); 74 | fieldDecl.children.length.should == 2; 75 | // This is why the issue was filed 76 | fieldDecl.type.getSizeof.should == -3; 77 | writelnUt(clang_getTemplateCursorKind(foo.cx)); 78 | 79 | const typeRef = fieldDecl.child(0); 80 | typeRef.shouldMatch(Cursor.Kind.TypeRef, "T"); 81 | typeRef.type.shouldMatch(Type.Kind.Unexposed, "T"); 82 | printChildren(typeRef); 83 | typeRef.children.length.should == 0; 84 | 85 | const intLiteral = fieldDecl.child(1); 86 | intLiteral.shouldMatch(Cursor.Kind.IntegerLiteral, ""); 87 | intLiteral.type.shouldMatch(Type.Kind.Int, "int"); 88 | printChildren(intLiteral); 89 | intLiteral.children.length.should == 0; 90 | } 91 | -------------------------------------------------------------------------------- /tests/contract/macro_.d: -------------------------------------------------------------------------------- 1 | module contract.macro_; 2 | 3 | 4 | import contract; 5 | 6 | 7 | @Tags("contract") 8 | @("macro after enum") 9 | @safe unittest { 10 | 11 | import clang: TranslationUnitFlags; 12 | import std.algorithm: countUntil; 13 | 14 | const tu = parse( 15 | C( 16 | ` 17 | enum TheEnum { BAR = 42 }; 18 | #define BAR 42 19 | ` 20 | ), 21 | TranslationUnitFlags.DetailedPreprocessingRecord, 22 | ); 23 | 24 | tu.children.length.shouldBeGreaterThan(2); 25 | 26 | const enumIndex = tu.children.countUntil!(a => a.kind == Cursor.Kind.EnumDecl && a.spelling == "TheEnum"); 27 | const macroIndex = tu.children.countUntil!(a => a.kind == Cursor.Kind.MacroDefinition && a.spelling == "BAR"); 28 | 29 | // for unfathomable reasons, clang puts all the macros at the top 30 | // completely disregarding the order they appear in the code 31 | enumIndex.shouldBeGreaterThan(macroIndex); 32 | } 33 | 34 | 35 | @Tags("contract") 36 | @("tokens") 37 | @safe unittest { 38 | import dpp.translation.macro_: isBuiltinMacro; 39 | import clang: TranslationUnitFlags, Token; 40 | import std.algorithm: filter; 41 | import std.array: array; 42 | 43 | const tu = parse( 44 | C( 45 | ` 46 | #define INT 42 47 | #define DOUBLE 33.3 48 | #define OCTAL 00177 49 | #define STRING "foobar" 50 | #define FUN(x) x 51 | #define GUN(y, ...) y(__VA_ARGS__) 52 | #define STR(x) #x 53 | ` 54 | ), 55 | TranslationUnitFlags.DetailedPreprocessingRecord, 56 | ); 57 | 58 | auto childrenRange = tu.children.filter!(a => !isBuiltinMacro(a)); 59 | const children = () @trusted { return childrenRange.array; }(); 60 | children.length.should == 7; 61 | 62 | const int_ = children[0]; 63 | int_.tokens.should == [ 64 | Token(Token.Kind.Identifier, "INT"), 65 | Token(Token.Kind.Literal, "42"), 66 | ]; 67 | 68 | const double_ = children[1]; 69 | double_.tokens.should == [ 70 | Token(Token.Kind.Identifier, "DOUBLE"), 71 | Token(Token.Kind.Literal, "33.3"), 72 | ]; 73 | 74 | const octal = children[2]; 75 | octal.tokens.should == [ 76 | Token(Token.Kind.Identifier, "OCTAL"), 77 | Token(Token.Kind.Literal, "00177"), 78 | ]; 79 | 80 | const string_ = children[3]; 81 | string_.tokens.should == [ 82 | Token(Token.Kind.Identifier, "STRING"), 83 | Token(Token.Kind.Literal, `"foobar"`), 84 | ]; 85 | 86 | const fun = children[4]; 87 | fun.tokens.should == [ 88 | Token(Token.Kind.Identifier, "FUN"), 89 | Token(Token.Kind.Punctuation, "("), 90 | Token(Token.Kind.Identifier, "x"), 91 | Token(Token.Kind.Punctuation, ")"), 92 | Token(Token.Kind.Identifier, "x"), 93 | ]; 94 | 95 | const gun = children[5]; 96 | gun.tokens.should == [ 97 | Token(Token.Kind.Identifier, "GUN"), 98 | Token(Token.Kind.Punctuation, "("), 99 | Token(Token.Kind.Identifier, "y"), 100 | Token(Token.Kind.Punctuation, ","), 101 | Token(Token.Kind.Punctuation, "..."), 102 | Token(Token.Kind.Punctuation, ")"), 103 | Token(Token.Kind.Identifier, "y"), 104 | Token(Token.Kind.Punctuation, "("), 105 | Token(Token.Kind.Identifier, "__VA_ARGS__"), 106 | Token(Token.Kind.Punctuation, ")"), 107 | ]; 108 | 109 | const str = children[6]; 110 | str.tokens.should == [ 111 | Token(Token.Kind.Identifier, "STR"), 112 | Token(Token.Kind.Punctuation, "("), 113 | Token(Token.Kind.Identifier, "x"), 114 | Token(Token.Kind.Punctuation, ")"), 115 | Token(Token.Kind.Punctuation, "#"), 116 | Token(Token.Kind.Identifier, "x"), 117 | ]; 118 | 119 | } 120 | -------------------------------------------------------------------------------- /tests/contract/main.d: -------------------------------------------------------------------------------- 1 | import unit_threaded.runner; 2 | 3 | mixin runTestsMain!( 4 | "contract.array", 5 | "contract.templates", 6 | "contract.namespace", 7 | "contract.macro_", 8 | "contract.constexpr", 9 | "contract.typedef_", 10 | "contract.operators", 11 | "contract.member", 12 | "contract.aggregates", 13 | "contract.inheritance", 14 | "contract.issues", 15 | "contract.methods", 16 | "contract.functions", 17 | "contract.enums", 18 | ); 19 | -------------------------------------------------------------------------------- /tests/contract/member.d: -------------------------------------------------------------------------------- 1 | /** 2 | Member pointers 3 | */ 4 | module contract.member; 5 | 6 | 7 | import contract; 8 | 9 | 10 | @Tags("contract") 11 | @("member object pointer") 12 | @safe unittest { 13 | const tu = parse( 14 | Cpp( 15 | q{ 16 | struct Struct { int i; }; 17 | int Struct::* globalStructInt; 18 | } 19 | ) 20 | ); 21 | 22 | tu.children.length.shouldEqual(2); 23 | 24 | const struct_ = tu.children[0]; 25 | struct_.kind.should == Cursor.Kind.StructDecl; 26 | 27 | const global = tu.children[1]; 28 | global.kind.should == Cursor.Kind.VarDecl; 29 | 30 | const globalType = global.type; 31 | globalType.kind.should == Type.Kind.MemberPointer; 32 | 33 | const pointee = globalType.pointee; 34 | pointee.kind.should == Type.Kind.Int; 35 | } 36 | 37 | 38 | 39 | @Tags("contract") 40 | @("private") 41 | @safe unittest { 42 | import clang: AccessSpecifier; 43 | 44 | const tu = parse( 45 | Cpp( 46 | q{ 47 | struct Struct { 48 | private: 49 | int i; 50 | public: 51 | int j; 52 | }; 53 | } 54 | ) 55 | ); 56 | 57 | tu.children.length.should == 1; 58 | const struct_ = tu.child(0); 59 | printChildren(struct_); 60 | struct_.children.length.should == 4; 61 | 62 | const private_ = struct_.child(0); 63 | private_.shouldMatch(Cursor.Kind.CXXAccessSpecifier, ""); 64 | private_.type.shouldMatch(Type.Kind.Invalid, ""); 65 | private_.children.length.should == 0; 66 | private_.accessSpecifier.should == AccessSpecifier.Private; 67 | 68 | const i = struct_.child(1); 69 | i.shouldMatch(Cursor.Kind.FieldDecl, "i"); 70 | i.type.shouldMatch(Type.Kind.Int, "int"); 71 | 72 | const public_ = struct_.child(2); 73 | public_.shouldMatch(Cursor.Kind.CXXAccessSpecifier, ""); 74 | public_.type.shouldMatch(Type.Kind.Invalid, ""); 75 | public_.children.length.should == 0; 76 | public_.accessSpecifier.should == AccessSpecifier.Public; 77 | 78 | const j = struct_.child(3); 79 | j.shouldMatch(Cursor.Kind.FieldDecl, "j"); 80 | j.type.shouldMatch(Type.Kind.Int, "int"); 81 | } 82 | -------------------------------------------------------------------------------- /tests/contract/methods.d: -------------------------------------------------------------------------------- 1 | module contract.methods; 2 | 3 | 4 | import contract; 5 | 6 | 7 | @("header") 8 | @safe unittest { 9 | 10 | const tu = parse( 11 | Cpp( 12 | q{ 13 | struct Foo { 14 | double fun(int i); 15 | }; 16 | 17 | double Foo::fun(int i) { 18 | return i * 2; 19 | } 20 | } 21 | ), 22 | ); 23 | 24 | tu.children.length.should == 2; 25 | 26 | const foo = tu.child(0); 27 | foo.shouldMatch(Cursor.Kind.StructDecl, "Foo"); 28 | 29 | const funDef = tu.child(1); 30 | funDef.shouldMatch(Cursor.Kind.CXXMethod, "fun"); 31 | funDef.type.shouldMatch(Type.Kind.FunctionProto, "double (int)"); 32 | printChildren(funDef); 33 | funDef.children.length.should == 3; 34 | writelnUt(funDef.tokens); 35 | 36 | const typeRef = funDef.child(0); 37 | typeRef.shouldMatch(Cursor.Kind.TypeRef, "struct Foo"); 38 | 39 | const param = funDef.child(1); 40 | param.shouldMatch(Cursor.Kind.ParmDecl, "i"); 41 | 42 | // this is where the magic happens 43 | const body_ = funDef.child(2); 44 | body_.shouldMatch(Cursor.Kind.CompoundStmt, ""); 45 | body_.type.shouldMatch(Type.Kind.Invalid, ""); 46 | printChildren(body_); 47 | body_.children.length.should == 1; 48 | 49 | const return_ = body_.child(0); 50 | return_.shouldMatch(Cursor.Kind.ReturnStmt, ""); 51 | return_.type.shouldMatch(Type.Kind.Invalid, ""); 52 | printChildren(return_); 53 | return_.children.length.should == 1; 54 | 55 | const doubleExpr = return_.child(0); 56 | doubleExpr.shouldMatch(Cursor.Kind.FirstExpr, ""); 57 | doubleExpr.type.shouldMatch(Type.Kind.Double, "double"); 58 | printChildren(doubleExpr); 59 | 60 | // it keeps going after this 61 | } 62 | -------------------------------------------------------------------------------- /tests/contract/namespace.d: -------------------------------------------------------------------------------- 1 | module contract.namespace; 2 | 3 | 4 | import contract; 5 | 6 | 7 | 8 | @Tags("contract") 9 | @("struct") 10 | @safe unittest { 11 | const tu = parse( 12 | Cpp( 13 | q{ 14 | namespace ns { 15 | struct Struct { 16 | 17 | }; 18 | } 19 | } 20 | ) 21 | ); 22 | 23 | tu.children.length.should == 1; 24 | const namespace = tu.children[0]; 25 | namespace.kind.should == Cursor.Kind.Namespace; 26 | 27 | namespace.children.length.should == 1; 28 | const struct_ = namespace.children[0]; 29 | struct_.kind.should == Cursor.Kind.StructDecl; 30 | 31 | struct_.spelling.should == "Struct"; 32 | struct_.type.spelling.should == "ns::Struct"; 33 | } 34 | 35 | 36 | @Tags("contract") 37 | @("template.type.parameter") 38 | @safe unittest { 39 | 40 | import dpp.clang: namespace; 41 | 42 | const tu = parse( 43 | Cpp( 44 | q{ 45 | namespace ns { 46 | struct Struct; 47 | template 48 | struct Template { }; 49 | class Class: public Template { 50 | int i; 51 | }; 52 | } 53 | } 54 | ) 55 | ); 56 | 57 | tu.children.length.should == 1; 58 | const ns = tu.child(0); 59 | ns.shouldMatch(Cursor.Kind.Namespace, "ns"); 60 | ns.type.shouldMatch(Type.Kind.Invalid, ""); 61 | 62 | printChildren(ns); 63 | ns.children.length.should == 3; 64 | 65 | const struct_ = ns.child(0); 66 | struct_.shouldMatch(Cursor.Kind.StructDecl, "Struct"); 67 | struct_.type.shouldMatch(Type.Kind.Record, "ns::Struct"); 68 | printChildren(struct_); 69 | struct_.children.length.should == 0; 70 | 71 | const template_ = ns.child(1); 72 | template_.shouldMatch(Cursor.Kind.ClassTemplate, "Template"); 73 | template_.type.shouldMatch(Type.Kind.Invalid, ""); 74 | printChildren(template_); 75 | template_.children.length.should == 1; 76 | 77 | const class_ = ns.child(2); 78 | class_.shouldMatch(Cursor.Kind.ClassDecl, "Class"); 79 | class_.type.shouldMatch(Type.Kind.Record, "ns::Class"); 80 | printChildren(class_); 81 | class_.children.length.should == 2; 82 | 83 | const base = class_.child(0); 84 | try 85 | base.shouldMatch(Cursor.Kind.CXXBaseSpecifier, "Template"); 86 | catch(Exception _) // libclang17 87 | base.shouldMatch(Cursor.Kind.CXXBaseSpecifier, "Template"); 88 | 89 | try 90 | base.type.shouldMatch(Type.Kind.Unexposed, "Template"); 91 | catch(Exception _) // libclang17 92 | base.type.shouldMatch(Type.Kind.Elaborated, "Template"); 93 | 94 | base.type.canonical.shouldMatch(Type.Kind.Record, "ns::Template"); 95 | printChildren(base); 96 | base.children.length.should == 2; 97 | 98 | const templateRef = base.child(0); 99 | templateRef.shouldMatch(Cursor.Kind.TemplateRef, "Template"); 100 | templateRef.type.shouldMatch(Type.Kind.Invalid, ""); 101 | templateRef.children.length.should == 0; 102 | 103 | const typeRef = base.child(1); 104 | typeRef.shouldMatch(Cursor.Kind.TypeRef, "struct ns::Struct"); 105 | typeRef.type.shouldMatch(Type.Kind.Record, "ns::Struct"); 106 | typeRef.type.canonical.shouldMatch(Type.Kind.Record, "ns::Struct"); 107 | typeRef.children.length.should == 0; 108 | 109 | const i = class_.child(1); 110 | i.shouldMatch(Cursor.Kind.FieldDecl, "i"); 111 | i.type.shouldMatch(Type.Kind.Int, "int"); 112 | i.children.length.should == 0; 113 | 114 | i.namespace.shouldMatch(Cursor.Kind.Namespace, "ns"); 115 | } 116 | -------------------------------------------------------------------------------- /tests/contract/operators.d: -------------------------------------------------------------------------------- 1 | module contract.operators; 2 | 3 | 4 | import contract; 5 | 6 | 7 | @("opCast no template") 8 | @safe unittest { 9 | 10 | const tu = parse( 11 | Cpp( 12 | q{ 13 | struct Struct { 14 | operator int() const; 15 | }; 16 | } 17 | ) 18 | ); 19 | 20 | tu.children.length.shouldEqual(1); 21 | 22 | const struct_ = tu.children[0]; 23 | printChildren(struct_); 24 | struct_.kind.should == Cursor.Kind.StructDecl; 25 | struct_.children.length.shouldEqual(1); 26 | 27 | const conversion = struct_.children[0]; 28 | printChildren(conversion); 29 | 30 | conversion.kind.should == Cursor.Kind.ConversionFunction; 31 | conversion.spelling.should == "operator int"; 32 | } 33 | 34 | @("opCast template") 35 | @safe unittest { 36 | 37 | const tu = parse( 38 | Cpp( 39 | q{ 40 | template 41 | struct Struct { 42 | operator T() const; 43 | }; 44 | } 45 | ) 46 | ); 47 | 48 | tu.children.length.shouldEqual(1); 49 | 50 | const struct_ = tu.children[0]; 51 | printChildren(struct_); 52 | struct_.kind.should == Cursor.Kind.ClassTemplate; 53 | struct_.children.length.shouldEqual(2); 54 | 55 | const typeParam = struct_.children[0]; 56 | typeParam.kind.should == Cursor.Kind.TemplateTypeParameter; 57 | 58 | const conversion = struct_.children[1]; 59 | printChildren(conversion); 60 | 61 | conversion.kind.should == Cursor.Kind.ConversionFunction; 62 | conversion.spelling.should == "operator type-parameter-0-0"; 63 | 64 | const retType = conversion.returnType; 65 | retType.kind.should == Type.Kind.Unexposed; 66 | retType.canonical.kind.should == Type.Kind.Unexposed; 67 | retType.spelling.should == "T"; 68 | retType.canonical.spelling.should == "type-parameter-0-0"; 69 | } 70 | -------------------------------------------------------------------------------- /tests/contract/typedef_.d: -------------------------------------------------------------------------------- 1 | module contract.typedef_; 2 | 3 | 4 | import contract; 5 | 6 | 7 | @("typedef to a template type parameter") 8 | @safe unittest { 9 | 10 | const tu = parse( 11 | Cpp( 12 | q{ 13 | template 14 | struct Struct { 15 | typedef T Type; 16 | }; 17 | } 18 | ) 19 | ); 20 | 21 | tu.children.length.shouldEqual(1); 22 | 23 | const struct_ = tu.children[0]; 24 | printChildren(struct_); 25 | 26 | struct_.kind.should == Cursor.Kind.ClassTemplate; 27 | struct_.children.length.should == 2; 28 | 29 | const typeParam = struct_.children[0]; 30 | typeParam.kind.should == Cursor.Kind.TemplateTypeParameter; 31 | 32 | const typedef_ = struct_.children[1]; 33 | typedef_.kind.should == Cursor.Kind.TypedefDecl; 34 | 35 | const underlyingType = typedef_.underlyingType; 36 | underlyingType.kind.should == Type.Kind.Unexposed; 37 | underlyingType.spelling.should == "T"; 38 | 39 | const canonicalUnderlyingType = underlyingType.canonical; 40 | canonicalUnderlyingType.kind.should == Type.Kind.Unexposed; 41 | canonicalUnderlyingType.spelling.should == "type-parameter-0-0"; 42 | } 43 | -------------------------------------------------------------------------------- /tests/it/c/compile/array.d: -------------------------------------------------------------------------------- 1 | module it.c.compile.array; 2 | 3 | import it; 4 | 5 | @("1d") 6 | unittest { 7 | shouldCompile( 8 | C( 9 | q{ 10 | struct Foo { int ints[4]; }; 11 | } 12 | ), 13 | 14 | D( 15 | q{ 16 | auto f = Foo(); 17 | static assert(f.sizeof == 16, "Wrong sizeof for Foo"); 18 | static assert(is(typeof(Foo.ints) == int[4])); 19 | } 20 | ), 21 | ); 22 | } 23 | 24 | @("flexible") 25 | unittest { 26 | shouldCompile( 27 | C( 28 | q{ 29 | struct Slice { 30 | int length; 31 | unsigned char arr[]; 32 | }; 33 | } 34 | ), 35 | 36 | D( 37 | q{ 38 | auto s = Slice(); 39 | static assert(s.sizeof == 4, "Wrong sizeof for Slice"); 40 | } 41 | ), 42 | ); 43 | } 44 | -------------------------------------------------------------------------------- /tests/it/c/compile/collision.d: -------------------------------------------------------------------------------- 1 | /** 2 | Tests for declarations that must be done at the end when they 3 | haven't appeared yet (due to pointers to undeclared structs) 4 | */ 5 | module it.c.compile.collision; 6 | 7 | import it; 8 | 9 | @Tags("collision") 10 | @("field of unknown struct pointer") 11 | @safe unittest { 12 | shouldCompile( 13 | C( 14 | q{ 15 | typedef struct Foo { 16 | struct Bar* bar; 17 | } Foo; 18 | } 19 | ), 20 | D( 21 | q{ 22 | Foo f; 23 | f.bar = null; 24 | } 25 | ), 26 | ); 27 | } 28 | 29 | @Tags("collision") 30 | @("unknown struct pointer return") 31 | @safe unittest { 32 | shouldCompile( 33 | C( 34 | q{ 35 | struct Foo* fun(int); 36 | } 37 | ), 38 | D( 39 | q{ 40 | auto f = fun(42); 41 | static assert(is(typeof(f) == Foo*)); 42 | } 43 | ), 44 | ); 45 | } 46 | 47 | @Tags("collision") 48 | @("unknown struct pointer param") 49 | @safe unittest { 50 | shouldCompile( 51 | C( 52 | q{ 53 | int fun(struct Foo* foo); 54 | } 55 | ), 56 | D( 57 | q{ 58 | Foo* foo; 59 | int i = fun(foo); 60 | } 61 | ), 62 | ); 63 | } 64 | 65 | 66 | @Tags("collision", "issue", "issue24") 67 | @("Old issue 24") 68 | @safe unittest { 69 | shouldCompile( 70 | C( 71 | q{ 72 | typedef struct _mailstream_low mailstream_low; 73 | struct mailstream_cancel* mailstream_low_get_cancel(void); 74 | struct _mailstream { 75 | struct mailstream_cancel* idle; 76 | }; 77 | 78 | struct mailstream_low_driver { 79 | void (*mailstream_cancel)(int); 80 | struct mailstream_cancel* (*mailstream_get_cancel)(mailstream_low*); 81 | }; 82 | 83 | int mailstream_low_wait_idle(struct mailstream_cancel*); 84 | 85 | struct _mailstream_low { 86 | void* data; 87 | struct mailstream_low_driver* driver; 88 | int privacy; 89 | char* identifier; 90 | unsigned long timeout; 91 | void* logger_context; 92 | }; 93 | } 94 | ), 95 | D( 96 | q{ 97 | // should just compile 98 | } 99 | ), 100 | ); 101 | } 102 | 103 | @Tags("collision") 104 | @("Undeclared struct pointer in function pointer field return type") 105 | @safe unittest { 106 | shouldCompile( 107 | C( 108 | q{ 109 | struct Struct { 110 | struct Foo* (*func)(void); 111 | }; 112 | } 113 | ), 114 | D( 115 | q{ 116 | Struct s; 117 | Foo* foo = s.func(); 118 | } 119 | ), 120 | ); 121 | } 122 | 123 | @Tags("collision") 124 | @("Undeclared struct pointer in function pointer field param type") 125 | @safe unittest { 126 | shouldCompile( 127 | C( 128 | q{ 129 | struct Struct { 130 | void (*func)(struct Foo*, struct Bar*); 131 | }; 132 | } 133 | ), 134 | D( 135 | q{ 136 | Foo* foo; 137 | Bar* bar; 138 | Struct s; 139 | s.func(foo, bar); 140 | } 141 | ), 142 | ); 143 | 144 | } 145 | 146 | 147 | @Tags("collision") 148 | @("foo and foo_ cause function foo to renamed as foo__") 149 | @safe unittest { 150 | shouldCompile( 151 | C( 152 | q{ 153 | void foo(void); 154 | // Struct causes the function to be named foo_ 155 | struct Struct { struct foo* field; }; 156 | struct foo_ { int dummy; }; 157 | } 158 | ), 159 | D( 160 | q{ 161 | Struct s; 162 | static assert(is(typeof(s.field) == foo*)); 163 | foo_ f; 164 | f.dummy = 42; 165 | foo__(); 166 | } 167 | ), 168 | ); 169 | } 170 | 171 | @Tags("collision") 172 | @("struct module and void module() should be renamed differently") 173 | @safe unittest { 174 | shouldCompile( 175 | C( 176 | q{ 177 | struct module; 178 | void module(struct module const * const ptr); 179 | struct module { struct module *module_; }; 180 | } 181 | ), 182 | D( 183 | q{ 184 | module_ md; 185 | md.module__ = &md; 186 | module__(md.module__); 187 | } 188 | ), 189 | ); 190 | } 191 | 192 | @Tags("collision") 193 | @("Accessors for members of anonymous records are renamed") 194 | @safe unittest { 195 | shouldCompile( 196 | C( 197 | q{ 198 | struct A { 199 | union { 200 | unsigned int version; 201 | char module; 202 | }; 203 | int a; 204 | }; 205 | } 206 | ), 207 | D( 208 | q{ 209 | A a; 210 | a.version_ = 7; 211 | a.module_ = 'D'; 212 | } 213 | ), 214 | ); 215 | } 216 | 217 | @Tags("collision") 218 | @("Members (pointers to struct) in multiple (possibly anon) structures") 219 | @safe unittest { 220 | shouldCompile( 221 | C( 222 | q{ 223 | struct A; 224 | 225 | struct B { 226 | struct A *A; 227 | }; 228 | 229 | struct C { 230 | struct A* A; 231 | }; 232 | 233 | struct D { 234 | union { 235 | struct A* A; 236 | int d; 237 | }; 238 | }; 239 | } 240 | ), 241 | D( 242 | q{ 243 | A *a; 244 | B b; 245 | b.A_ = a; 246 | C c; 247 | c.A_ = a; 248 | D d; 249 | d.A_ = a; 250 | } 251 | ), 252 | ); 253 | } 254 | -------------------------------------------------------------------------------- /tests/it/c/compile/extensions.d: -------------------------------------------------------------------------------- 1 | /** 2 | Non-standard language extensions 3 | */ 4 | module it.c.compile.extensions; 5 | 6 | 7 | import it; 8 | 9 | 10 | @HiddenTest // used to pass now fails, not sure how to make clang parse it right 11 | @("typeof.funcdecl") 12 | @safe unittest { 13 | shouldCompile( 14 | C( 15 | q{ 16 | struct Foo; 17 | // typeof is a gcc and clang language extension 18 | typeof(struct Foo *) func(); 19 | } 20 | ), 21 | 22 | D( 23 | q{ 24 | import std.traits: ReturnType; 25 | static assert(is(ReturnType!func == Foo*)); 26 | } 27 | ), 28 | ); 29 | } 30 | 31 | @HiddenTest // used to pass now fails, not sure how to make clang parse it right 32 | @("typeof.cast") 33 | @safe unittest { 34 | shouldCompile( 35 | C( 36 | ` 37 | #define DUMMY(x) (sizeof((typeof(x) *)1)) 38 | ` 39 | ), 40 | 41 | D( 42 | q{ 43 | auto a = DUMMY(7); 44 | } 45 | ), 46 | ); 47 | } 48 | -------------------------------------------------------------------------------- /tests/it/c/compile/function_.d: -------------------------------------------------------------------------------- 1 | module it.c.compile.function_; 2 | 3 | import it; 4 | 5 | @("nn_strerror") 6 | @safe unittest { 7 | shouldCompile( 8 | C( 9 | q{ 10 | const char *nn_strerror (int errnum); 11 | } 12 | ), 13 | 14 | D( 15 | q{ 16 | int err = 42; 17 | const(char)* str = nn_strerror(err); 18 | } 19 | ), 20 | ); 21 | } 22 | 23 | 24 | @("int function(const(char)*)") 25 | @safe unittest { 26 | shouldCompile( 27 | C( 28 | q{ 29 | typedef int (*function_t)(const char*); 30 | } 31 | ), 32 | D( 33 | q{ 34 | int ret = function_t.init("foobar".ptr); 35 | } 36 | ), 37 | ); 38 | } 39 | 40 | @("const(char)* function(double, int)") 41 | @safe unittest { 42 | shouldCompile( 43 | C( 44 | q{ 45 | typedef const char* (*function_t)(double, int); 46 | } 47 | 48 | ), 49 | D( 50 | q{ 51 | const(char)* ret = function_t.init(33.3, 42); 52 | } 53 | ), 54 | ); 55 | } 56 | 57 | @("void function()") 58 | @safe unittest { 59 | shouldCompile( 60 | C( 61 | q{ 62 | typedef void (*function_t)(void); 63 | } 64 | ), 65 | D( 66 | q{ 67 | import std.traits; 68 | import std.meta; 69 | auto f = function_t(); 70 | static assert(is(ReturnType!f == void)); 71 | static assert(is(Parameters!f == AliasSeq!())); 72 | } 73 | ), 74 | ); 75 | } 76 | 77 | @("variadic") 78 | @safe unittest { 79 | shouldCompile( 80 | C( 81 | q{ 82 | void fun(int, ...); 83 | } 84 | ), 85 | D( 86 | q{ 87 | fun(42); 88 | fun(42, 33); 89 | fun(42, 33.3); 90 | fun(42, "foobar".ptr); 91 | } 92 | ), 93 | ); 94 | } 95 | 96 | @("old uts") 97 | @safe unittest { 98 | shouldCompile( 99 | C( 100 | q{ 101 | struct Foo { int value; }; 102 | struct Bar { int value; }; 103 | struct Foo addFoos(struct Foo* foo1, struct Foo* foo2); 104 | struct Bar addBars(const struct Bar* bar1, const struct Bar* bar2); 105 | const char *nn_strerror (int errnum); 106 | } 107 | ), 108 | D( 109 | q{ 110 | 111 | auto f1 = Foo(2); 112 | auto f2 = Foo(3); 113 | Foo f = addFoos(&f1, &f2); 114 | const b1 = Bar(2); 115 | const b2 = Bar(3); 116 | Bar b = addBars(&b1, &b2); 117 | const(char*) msg = nn_strerror(42); 118 | } 119 | ), 120 | ); 121 | } 122 | 123 | @("unexposed function pointer variable") 124 | @safe unittest { 125 | shouldCompile( 126 | C( 127 | q{ 128 | int (*func_t) (int, int); 129 | } 130 | ), 131 | D( 132 | q{ 133 | int res = func_t.init(2, 3); 134 | } 135 | ), 136 | ); 137 | } 138 | 139 | 140 | @("enum param") 141 | @safe unittest { 142 | shouldCompile( 143 | C( 144 | q{ 145 | enum Enum { foo, bar, baz }; 146 | void fun(enum Enum e); 147 | enum Enum gun(int i); 148 | } 149 | ), 150 | D( 151 | q{ 152 | fun(Enum.foo); 153 | Enum ret = gun(42); 154 | } 155 | ), 156 | ); 157 | } 158 | 159 | @("typedef enum param") 160 | @safe unittest { 161 | shouldCompile( 162 | C( 163 | q{ 164 | typedef enum { foo, bar, baz } Enum; 165 | void fun(Enum e); 166 | Enum gun(int i); 167 | } 168 | ), 169 | D( 170 | q{ 171 | fun(Enum.foo); 172 | Enum ret = gun(42); 173 | } 174 | ), 175 | ); 176 | } 177 | 178 | @("enum param function pointer") 179 | @safe unittest { 180 | shouldCompile( 181 | C( 182 | q{ 183 | enum Enum { foo, bar, baz }; 184 | typedef void (*fun)(enum Enum e); 185 | typedef enum Enum (*gun)(int i); 186 | } 187 | ), 188 | D( 189 | q{ 190 | fun.init(Enum.foo); 191 | Enum ret = gun.init(42); 192 | } 193 | ), 194 | ); 195 | } 196 | 197 | @("typedef enum param function pointer") 198 | @safe unittest { 199 | shouldCompile( 200 | C( 201 | q{ 202 | typedef enum { foo, bar, baz } Enum; 203 | typedef void (*fun)(Enum e); 204 | typedef Enum (*gun)(int i); 205 | } 206 | ), 207 | D( 208 | q{ 209 | fun.init(Enum.foo); 210 | Enum ret = gun.init(42); 211 | } 212 | ), 213 | ); 214 | } 215 | 216 | @("enum param function var") 217 | @safe unittest { 218 | shouldCompile( 219 | C( 220 | q{ 221 | enum Enum { foo, bar, baz }; 222 | void (*fun)(enum Enum e); 223 | enum Enum (*gun)(int i); 224 | } 225 | ), 226 | D( 227 | q{ 228 | fun(Enum.foo); 229 | Enum ret = gun(42); 230 | } 231 | ), 232 | ); 233 | } 234 | 235 | @("typedef enum param function var") 236 | @safe unittest { 237 | shouldCompile( 238 | C( 239 | q{ 240 | typedef enum { foo, bar, baz } Enum; 241 | void (*fun)(Enum e); 242 | Enum (*gun)(int i); 243 | } 244 | ), 245 | D( 246 | q{ 247 | fun(Enum.foo); 248 | Enum ret = gun(42); 249 | } 250 | ), 251 | ); 252 | } 253 | 254 | 255 | @("return pointer to const struct") 256 | @safe unittest { 257 | shouldCompile( 258 | C( 259 | q{ 260 | typedef struct Foo { int dummy; } Foo; 261 | const Foo* create_foo(int dummy); 262 | } 263 | ), 264 | D( 265 | q{ 266 | const(Foo)* foo = create_foo(42); 267 | } 268 | ), 269 | ); 270 | 271 | } 272 | 273 | @Tags("FunctionNoProto") 274 | @("function pointer with no parameter types return type unknown struct") 275 | @safe unittest { 276 | shouldCompile( 277 | C( 278 | q{ 279 | struct Struct { 280 | struct Foo* (*func)(); 281 | }; 282 | } 283 | ), 284 | D( 285 | q{ 286 | Struct s; 287 | Foo* foo = s.func(); 288 | } 289 | ), 290 | ); 291 | } 292 | 293 | @Tags("FunctionNoProto") 294 | @("function pointer with no parameter types return type int") 295 | @safe unittest { 296 | shouldCompile( 297 | C( 298 | q{ 299 | struct Struct { 300 | int (*func)(); 301 | }; 302 | } 303 | ), 304 | D( 305 | q{ 306 | Struct s; 307 | int i = s.func(); 308 | } 309 | ), 310 | ); 311 | } 312 | 313 | 314 | 315 | @("@nogc nothrow") 316 | unittest { 317 | shouldCompile( 318 | C( 319 | q{ 320 | void fun(void); 321 | } 322 | ), 323 | D( 324 | q{ 325 | static void wrapper() @nogc nothrow { 326 | fun(); 327 | } 328 | } 329 | ), 330 | ); 331 | } 332 | 333 | 334 | @("__printf") 335 | unittest { 336 | shouldCompile( 337 | C( 338 | ` 339 | #define __printf(a, b) __attribute__((format(printf, a, b))) 340 | __printf(1, 2) 341 | void myprintf(const char* fmt, ...); 342 | ` 343 | ), 344 | D( 345 | q{ 346 | auto foo = 42; 347 | myprintf("%d", foo); 348 | } 349 | ), 350 | ); 351 | } 352 | -------------------------------------------------------------------------------- /tests/it/c/compile/preprocessor.d: -------------------------------------------------------------------------------- 1 | module it.c.compile.preprocessor; 2 | 3 | 4 | import it; 5 | 6 | 7 | @("simple macro") 8 | @safe unittest { 9 | shouldCompile( 10 | C( 11 | ` 12 | #define FOO 5 13 | ` 14 | ), 15 | D( 16 | q{ 17 | int[FOO] foos; 18 | static assert(foos.length == 5, "Wrong length for foos"); 19 | } 20 | ) 21 | ); 22 | } 23 | 24 | @("define macro, undefine, then define again") 25 | @safe unittest { 26 | shouldCompile( 27 | C( 28 | ` 29 | #define FOO foo 30 | #undef FOO 31 | #define FOO bar 32 | int FOO(int i); 33 | ` 34 | ), 35 | D( 36 | q{ 37 | int i = bar(2); 38 | } 39 | ) 40 | ); 41 | } 42 | 43 | 44 | @("include guards") 45 | @safe unittest { 46 | with(immutable IncludeSandbox()) { 47 | writeFile("hdr.h", 48 | `#ifndef HAHA 49 | # define HAHA 50 | int inc(int); 51 | #endif`); 52 | writeFile("foo.dpp", 53 | `#include "hdr.h" 54 | import bar; 55 | int func(int i) { return inc(i) * 2; }`); 56 | writeFile("bar.dpp", 57 | `#include "hdr.h"; 58 | int func(int i) { return inc(i) * 3; }`); 59 | 60 | runPreprocessOnly("foo.dpp"); 61 | runPreprocessOnly("bar.dpp"); 62 | shouldCompile("foo.d"); 63 | } 64 | } 65 | 66 | 67 | @("octal.whitespace") 68 | @safe unittest { 69 | shouldCompile( 70 | C( 71 | ` 72 | #define FOO 00 73 | ` 74 | ), 75 | D( 76 | q{ 77 | } 78 | ) 79 | ); 80 | 81 | } 82 | 83 | 84 | @("elaborate") 85 | @safe unittest { 86 | shouldCompile( 87 | C( 88 | ` 89 | struct Foo {}; 90 | #define STRUCT_HEAD \ 91 | int count; \ 92 | struct Foo *foo; 93 | ` 94 | ), 95 | D( 96 | q{ 97 | static struct Derived { 98 | STRUCT_HEAD 99 | } 100 | 101 | auto d = Derived(); 102 | d.count = 42; 103 | d.foo = null; 104 | } 105 | ) 106 | ); 107 | } 108 | 109 | 110 | @("multiline") 111 | @safe unittest { 112 | shouldCompile( 113 | C( 114 | ` 115 | // WARNING: don't attempt to tidy up the formatting here or the 116 | // test is actually changed 117 | #define void_to_int_ptr(x) ( \ 118 | (int *) x \ 119 | ) 120 | ` 121 | ), 122 | D( 123 | q{ 124 | import std.stdio: writeln; 125 | int a = 7; 126 | void *p = &a; 127 | auto intPtr = void_to_int_ptr(p); 128 | } 129 | ) 130 | ); 131 | } 132 | 133 | @("func") 134 | @safe unittest { 135 | with(immutable IncludeSandbox()) { 136 | writeFile("hdr.h", 137 | `#define FOO(x) ((x) * 2) 138 | #define BAR(x, y) ((x) + (y)) 139 | #define BAZ(prefix, ...) text(prefix, __VA_ARGS__) 140 | #define STR(x) #x 141 | #define ARGH(x) STR(I like spaces x) 142 | #define NOARGS() ((short) 42)`); 143 | 144 | writeFile("foo.dpp", 145 | [`#include "hdr.h"`, 146 | `import std.conv : text;`]); 147 | writeFile("bar.d", 148 | q{ 149 | import foo; 150 | static assert(FOO(2) == 4); 151 | static assert(FOO(3) == 6); 152 | static assert(BAR(2, 3) == 5); 153 | static assert(BAR(3, 4) == 7); 154 | static assert(BAZ("prefix_", 42, "foo") == "prefix_42foo"); 155 | static assert(BAZ("prefix_", 42, "foo", "bar") == "prefix_42foobar"); 156 | static assert(NOARGS() == 42); 157 | }); 158 | 159 | runPreprocessOnly("--function-macros", "foo.dpp"); 160 | shouldCompile("foo.d"); 161 | shouldCompile("bar.d"); 162 | } 163 | } 164 | 165 | @("cast.param") 166 | @safe unittest { 167 | shouldCompile( 168 | C( 169 | ` 170 | #define MEMBER_SIZE(T, member) sizeof(((T*)0)-> member) 171 | struct Struct { int i; }; 172 | ` 173 | ), 174 | D( 175 | q{ 176 | static assert(MEMBER_SIZE(Struct, i) == 4); 177 | } 178 | ), 179 | ); 180 | } 181 | 182 | @("cast.uchar") 183 | @safe unittest { 184 | shouldCompile( 185 | C( 186 | ` 187 | #define CHAR_MASK(c) ((unsigned char)((c) & 0xff)) 188 | ` 189 | ), 190 | D( 191 | q{ 192 | static assert(CHAR_MASK(0xab) == 0xab); 193 | static assert(CHAR_MASK(0xf1) == 0xf1); 194 | static assert(CHAR_MASK(0x1f) == 0x1f); 195 | static assert(CHAR_MASK(0xff) == 0xff); 196 | } 197 | ), 198 | ); 199 | } 200 | 201 | @("dowhile") 202 | @safe unittest { 203 | shouldCompile( 204 | C( 205 | ` 206 | #define Py_BUILD_ASSERT(cond) do { \ 207 | (void)Py_BUILD_ASSERT_EXPR(cond); \ 208 | } while(0) 209 | ` 210 | ), 211 | D( 212 | q{ 213 | } 214 | ), 215 | ["--function-macros"], 216 | ); 217 | 218 | } 219 | 220 | @("vartype") 221 | @safe unittest { 222 | shouldCompile( 223 | C( 224 | ` 225 | #define DOC_VAR(name) static const char name[] 226 | ` 227 | ), 228 | D( 229 | q{ 230 | } 231 | ), 232 | ["--function-macros"], 233 | ); 234 | } 235 | -------------------------------------------------------------------------------- /tests/it/c/compile/runtime_args.d: -------------------------------------------------------------------------------- 1 | /** 2 | Tests for runtime arguments. 3 | */ 4 | module it.c.compile.runtime_args; 5 | 6 | 7 | import it; 8 | 9 | 10 | @("include paths absolute") 11 | @safe unittest { 12 | with(immutable IncludeSandbox()) { 13 | writeFile("includes/hdr.h", 14 | q{ 15 | int add(int i, int j); 16 | }); 17 | writeFile("main.dpp", 18 | ` 19 | #include "hdr.h" 20 | void main() { 21 | int ret = add(2, 3); 22 | } 23 | `); 24 | runPreprocessOnly( 25 | "--include-path", 26 | inSandboxPath("includes"), 27 | "main.dpp", 28 | ); 29 | 30 | shouldCompile("main.d"); 31 | } 32 | } 33 | 34 | 35 | @("include paths relative") 36 | @safe unittest { 37 | with(immutable IncludeSandbox()) { 38 | writeFile("includes/hdr.h", 39 | q{ 40 | int add(int i, int j); 41 | }); 42 | writeFile("main.dpp", 43 | ` 44 | #include "hdr.h" 45 | void main() { 46 | int ret = add(2, 3); 47 | } 48 | `); 49 | runPreprocessOnly( 50 | "--include-path", 51 | "includes", // notice the lack of `inSandboxPath` 52 | "main.dpp", 53 | ); 54 | 55 | shouldCompile("main.d"); 56 | } 57 | } 58 | 59 | @("--ignore-macros") 60 | @safe unittest { 61 | with(immutable IncludeSandbox()) { 62 | writeFile("includes/hdr.h", 63 | q{ 64 | int add(int i, int j); 65 | }); 66 | writeFile("main.dpp", 67 | ` 68 | #include "hdr.h" 69 | void main() { 70 | int ret = add(10, 20); 71 | } 72 | `); 73 | runPreprocessOnly( 74 | "--include-path", 75 | "includes", 76 | "--ignore-macros", 77 | "main.dpp", 78 | ); 79 | 80 | shouldCompile("main.d"); 81 | } 82 | } 83 | 84 | 85 | @("src output path") 86 | @safe unittest { 87 | with(immutable IncludeSandbox()) { 88 | writeFile("hdr.h", 89 | q{ 90 | int add(int i, int j); 91 | }); 92 | writeFile("main.dpp", 93 | ` 94 | #include "hdr.h" 95 | void main() { 96 | int ret = add(2, 3); 97 | } 98 | `); 99 | runPreprocessOnly( 100 | "--source-output-path", 101 | inSandboxPath("foo/bar"), 102 | "main.dpp", 103 | ); 104 | 105 | shouldCompile(inSandboxPath("foo/bar/main.d")); 106 | } 107 | } 108 | 109 | 110 | @("rewritten module name") 111 | @safe unittest { 112 | with(immutable IncludeSandbox()) { 113 | writeFile("pretend-windows.h", 114 | ``); 115 | writeFile("hdr.h", 116 | ` 117 | #include "pretend-windows.h" 118 | `); 119 | writeFile("main.dpp", 120 | ` 121 | #include "hdr.h" 122 | void main() { 123 | writeln(""); // should have imported by include translation 124 | } 125 | `); 126 | runPreprocessOnly( 127 | "--prebuilt-header", 128 | "pretend-windows.h=std.stdio", 129 | "main.dpp", 130 | ); 131 | 132 | shouldCompile("main.d"); 133 | } 134 | } 135 | 136 | 137 | @("ignored paths") 138 | @safe unittest { 139 | with(immutable IncludeSandbox()) { 140 | writeFile("pretend-windows.h", 141 | ` 142 | #define foo "foo" 143 | `); 144 | writeFile("hdr.h", 145 | ` 146 | #include "pretend-windows.h" 147 | `); 148 | writeFile("main.dpp", 149 | ` 150 | #include "hdr.h" 151 | void main() { 152 | static assert(!is(typeof(foo))); 153 | } 154 | `); 155 | runPreprocessOnly( 156 | "--ignore-path", 157 | "*pretend-windows.h", 158 | "main.dpp", 159 | ); 160 | 161 | shouldCompile("main.d"); 162 | } 163 | } 164 | 165 | 166 | @("ignored system paths") 167 | @safe unittest { 168 | with(immutable IncludeSandbox()) { 169 | writeFile("main.dpp", 170 | ` 171 | #include 172 | // since the system path is ignored, the declarations 173 | // should NOT have been generated here 174 | void main() { 175 | static assert(!is(typeof(printf))); 176 | } 177 | `); 178 | runPreprocessOnly( 179 | "--ignore-system-paths", 180 | "main.dpp", 181 | ); 182 | 183 | shouldCompile("main.d"); 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /tests/it/c/compile/struct_.d: -------------------------------------------------------------------------------- 1 | module it.c.compile.struct_; 2 | 3 | 4 | import it; 5 | 6 | 7 | @("onefield.int") 8 | @C( 9 | q{ 10 | struct Foo { int i; }; 11 | } 12 | ) 13 | @safe unittest { 14 | mixin( 15 | shouldCompile( 16 | D( 17 | q{ 18 | auto f = Foo(5); 19 | static assert(f.sizeof == 4, "Wrong sizeof for foo"); 20 | } 21 | ) 22 | ) 23 | ); 24 | } 25 | 26 | @("onefield.double") 27 | @safe unittest { 28 | shouldCompile( 29 | C( 30 | q{ 31 | struct Bar { double d; }; 32 | } 33 | ), 34 | 35 | D( 36 | q{ 37 | auto b = Bar(33.3); 38 | static assert(b.sizeof == 8, "Wrong sizeof for Bar"); 39 | } 40 | ) 41 | ); 42 | } 43 | 44 | 45 | @("threefields") 46 | @safe unittest { 47 | shouldCompile( 48 | C( 49 | q{ 50 | struct Baz { 51 | int i; 52 | int j; 53 | double d; 54 | }; 55 | } 56 | ), 57 | 58 | D( 59 | q{ 60 | import std.conv: text; 61 | auto b = Baz(42, 7, 33.3); 62 | static assert(is(typeof(b.i) == int)); 63 | static assert(is(typeof(b.j) == int)); 64 | static assert(is(typeof(b.d) == double)); 65 | static assert(b.sizeof == 16, text("Wrong sizeof for Baz: ", b.sizeof)); 66 | } 67 | ) 68 | ); 69 | } 70 | 71 | 72 | @("nested") 73 | @C( 74 | q{ 75 | struct Outer { 76 | int integer; 77 | struct Inner { 78 | int x; 79 | } inner; 80 | }; 81 | } 82 | ) 83 | @safe unittest { 84 | mixin( 85 | shouldCompile( 86 | D( 87 | q{ 88 | auto o = Outer(77, Outer.Inner(42)); 89 | static assert(o.sizeof == 8, "Wrong sizeof for Outer"); 90 | } 91 | ) 92 | ) 93 | ); 94 | } 95 | 96 | 97 | @("typedef.name") 98 | @C( 99 | q{ 100 | typedef struct TypeDefd_ { 101 | int i; 102 | double d; 103 | } TypeDefd; 104 | } 105 | ) 106 | @safe unittest { 107 | mixin( 108 | shouldCompile( 109 | D( 110 | q{ 111 | { 112 | auto t = TypeDefd_(42, 33.3); 113 | static assert(t.sizeof == 16, "Wrong sizeof for TypeDefd_"); 114 | } 115 | { 116 | auto t = TypeDefd(42, 33.3); 117 | static assert(t.sizeof == 16, "Wrong sizeof for TypeDefd"); 118 | } 119 | } 120 | ) 121 | ) 122 | ); 123 | } 124 | 125 | 126 | @C( 127 | q{ 128 | typedef struct { 129 | int x, y, z; 130 | } Nameless1; 131 | 132 | typedef struct { 133 | double d; 134 | } Nameless2; 135 | } 136 | ) 137 | @("typedef.anon") 138 | @safe unittest { 139 | mixin( 140 | shouldCompile( 141 | D( 142 | q{ 143 | auto n1 = Nameless1(2, 3, 4); 144 | static assert(n1.sizeof == 12, "Wrong sizeof for Nameless1"); 145 | static assert(is(typeof(Nameless1.x) == int)); 146 | static assert(is(typeof(Nameless1.y) == int)); 147 | static assert(is(typeof(Nameless1.z) == int)); 148 | 149 | auto n2 = Nameless2(33.3); 150 | static assert(n2.sizeof == 8, "Wrong sizeof for Nameless2"); 151 | static assert(is(typeof(Nameless2.d) == double)); 152 | } 153 | ) 154 | ) 155 | ); 156 | } 157 | 158 | 159 | @C( 160 | q{ 161 | typedef struct A B; 162 | struct A { int a; }; 163 | } 164 | ) 165 | @("typedef.before") 166 | @safe unittest { 167 | mixin( 168 | shouldCompile( 169 | D( 170 | q{ 171 | auto a = A(42); 172 | auto b = B(77); 173 | static assert(is(A == B)); 174 | } 175 | ) 176 | ) 177 | ); 178 | } 179 | 180 | 181 | @WIP2 182 | @("fsid_t") 183 | @safe unittest { 184 | shouldCompile( 185 | C( 186 | ` 187 | #define __FSID_T_TYPE struct { int __val[2]; } 188 | typedef __FSID_T_TYPE __fsid_t; 189 | typedef __fsid_t fsid_t; 190 | ` 191 | ), 192 | D( 193 | q{ 194 | fsid_t foo; 195 | foo.__val[0] = 2; 196 | foo.__val[1] = 3; 197 | } 198 | ) 199 | ); 200 | } 201 | 202 | 203 | @WIP2 204 | @("fd_set") 205 | @safe unittest { 206 | 207 | with(immutable IncludeSandbox()) { 208 | 209 | writeFile("system.h", 210 | ` 211 | #define __FD_SETSIZE 1024 212 | typedef long int __fd_mask; 213 | #define __NFDBITS (8 * (int) sizeof (__fd_mask)) 214 | 215 | typedef struct 216 | { 217 | #ifdef __USE_XOPEN 218 | __fd_mask fds_bits[__FD_SETSIZE / __NFDBITS]; 219 | # define __FDS_BITS(set) ((set)->fds_bits) 220 | #else 221 | __fd_mask __fds_bits[__FD_SETSIZE / __NFDBITS]; 222 | # define __FDS_BITS(set) ((set)->__fds_bits) 223 | #endif 224 | } fd_set; 225 | `); 226 | 227 | 228 | writeFile("header.h", 229 | ` 230 | #include "system.h" 231 | `); 232 | 233 | const dppFileName = "foo.dpp"; 234 | writeFile("foo.dpp", 235 | ` 236 | #include "header.h" 237 | void func() { 238 | fd_set foo; 239 | foo.__fds_bits[0] = 5; 240 | } 241 | `); 242 | 243 | 244 | runPreprocessOnly("foo.dpp"); 245 | shouldCompile("foo.d"); 246 | } 247 | } 248 | 249 | 250 | @("multiple declarations") 251 | @safe unittest { 252 | shouldCompile( 253 | C( 254 | q{ 255 | struct Struct; 256 | struct Struct; 257 | struct OtherStruct; 258 | struct Struct { int x, y, z; }; 259 | } 260 | ), 261 | D( 262 | q{ 263 | Struct s; 264 | s.x = 42; 265 | s.y = 33; 266 | s.z = 77; 267 | static assert(!__traits(compiles, OtherStruct())); 268 | } 269 | ) 270 | ); 271 | } 272 | 273 | 274 | @WIP2 275 | @("var.anonymous.notypedef") 276 | @safe unittest { 277 | shouldCompile( 278 | C(`struct { int i; } var;`), 279 | D( 280 | q{ 281 | var.i = 42; 282 | } 283 | ) 284 | ); 285 | } 286 | 287 | 288 | @WIP2 289 | @("var.anonymous.typedef") 290 | @safe unittest { 291 | shouldCompile( 292 | C(` 293 | typedef struct { int i; } mystruct; 294 | mystruct var; 295 | `), 296 | D( 297 | q{ 298 | var.i = 42; 299 | } 300 | ) 301 | ); 302 | } 303 | 304 | @("const anonymous struct as field") 305 | @safe unittest { 306 | shouldCompile( 307 | C( 308 | q{ 309 | struct A { 310 | const struct { 311 | int version; 312 | int other; 313 | } version; 314 | }; 315 | } 316 | ), 317 | D( 318 | q{ 319 | A a = { version_ : { version_ : 13, other : 7 } }; 320 | } 321 | ) 322 | ); 323 | } 324 | 325 | @("Pointer to pointer to undeclared struct should result in a struct declaration") 326 | @safe unittest { 327 | shouldCompile( 328 | C( 329 | q{ 330 | struct A { 331 | const struct B** p; 332 | }; 333 | 334 | void f(struct C***); 335 | } 336 | ), 337 | D( 338 | q{ 339 | B *ptrB; 340 | C *ptrC; 341 | } 342 | ) 343 | ); 344 | } 345 | 346 | 347 | @("rpcndr") 348 | @safe unittest { 349 | shouldCompile( 350 | C( 351 | q{ 352 | typedef struct 353 | { 354 | int i; 355 | } *StructPtr; 356 | } 357 | ), 358 | D( 359 | q{ 360 | StructPtr ptr = null; 361 | } 362 | ) 363 | ); 364 | } 365 | -------------------------------------------------------------------------------- /tests/it/c/compile/typedef_.d: -------------------------------------------------------------------------------- 1 | module it.c.compile.typedef_; 2 | 3 | import it; 4 | 5 | // 0 children 6 | // underlying: UChar("unsigned char") 7 | // underlying canonical: UChar("unsigned char") 8 | @("unsigned char") 9 | unittest { 10 | shouldCompile( 11 | C( 12 | q{ 13 | typedef unsigned char __u_char; 14 | } 15 | ), 16 | D( 17 | q{ 18 | static assert(__u_char.sizeof == 1); 19 | } 20 | ) 21 | ); 22 | } 23 | 24 | // 0 children 25 | // underlying: Pointer("const char *") 26 | // underlying canonical: Pointer("const char *") 27 | @("const char*") 28 | unittest { 29 | shouldCompile( 30 | C( 31 | q{ 32 | typedef const char* mystring; 33 | } 34 | ), 35 | D( 36 | q{ 37 | const(char)[128] buffer; 38 | } 39 | ) 40 | ); 41 | } 42 | 43 | // 1 child: StructDecl(""), Type.Record("Foo") 44 | // underlying: Elaborated("struct Foo") 45 | // underlying canonical: Record("Foo") 46 | @("anonymous struct") 47 | unittest { 48 | shouldCompile( 49 | C( 50 | q{ 51 | typedef struct { int i; } Foo; 52 | } 53 | ), 54 | D( 55 | q{ 56 | Foo f; 57 | f.i = 42; 58 | static assert(!__traits(compiles, _Anonymous_1(42))); 59 | } 60 | ) 61 | ); 62 | } 63 | 64 | // 1 child, StructDecl("Foo"), Type.Record("struct Foo") 65 | // underlying: Elaborated("struct Foo") 66 | // underlying canonical: Record("struct Foo") 67 | @("struct") 68 | unittest { 69 | shouldCompile( 70 | C( 71 | q{ 72 | typedef struct Foo { int i; } Foo; 73 | } 74 | ), 75 | D( 76 | q{ 77 | Foo f1; 78 | f1.i = 42; 79 | Foo f2; 80 | f2.i = 33; 81 | } 82 | ) 83 | ); 84 | } 85 | 86 | // 1 child, UnionDecl("Foo"), Type.Record("union Foo") 87 | // underlying: Elaborated("union Foo") 88 | // underlying canonical: Record("union Foo") 89 | @("union") 90 | unittest { 91 | shouldCompile( 92 | C( 93 | q{ 94 | typedef union Foo { int i; } Foo; 95 | } 96 | ), 97 | D( 98 | q{ 99 | Foo f1; 100 | f1.i = 42; 101 | Foo f2; 102 | f2.i = 33; 103 | } 104 | ) 105 | ); 106 | } 107 | 108 | // 1 child, EnumDecl("Foo"), Type.Enum("enum Foo") 109 | // underlying: Elaborated("enum Foo") 110 | // underlying canonical: Enum("enum Foo") 111 | @("enum") 112 | unittest { 113 | shouldCompile( 114 | C( 115 | q{ 116 | typedef enum Foo { i } Foo; 117 | } 118 | ), 119 | D( 120 | q{ 121 | Foo f = Foo.i; 122 | } 123 | ) 124 | ); 125 | } 126 | 127 | 128 | // 1 child, IntegerLiteral(""), Type.Int("int") 129 | // underlying: ConstantArray("int [42]") 130 | // underlying canonical: ConstantArray("int [42]") 131 | @("array") 132 | unittest { 133 | shouldCompile( 134 | C( 135 | q{ 136 | typedef int Array[42]; 137 | } 138 | ), 139 | D( 140 | q{ 141 | Array a; 142 | static assert(a.sizeof == 42 * int.sizeof); 143 | } 144 | ) 145 | ); 146 | } 147 | 148 | // 0 children 149 | // underling: Pointer("int *") 150 | // underlying canonical: Pointer("int *") 151 | @("pointer") 152 | unittest { 153 | shouldCompile( 154 | C( 155 | q{ 156 | typedef int* IntPtr; 157 | } 158 | ), 159 | D( 160 | q{ 161 | int i = 42; 162 | IntPtr ptr = &i; 163 | } 164 | ) 165 | ); 166 | } 167 | 168 | @("typedef multiple definitions") 169 | @safe unittest { 170 | // See https://github.com/tpn/winsdk-10/blob/9b69fd26ac0c7d0b83d378dba01080e93349c2ed/Include/10.0.16299.0/um/winscard.h#L504-L528 171 | with(immutable IncludeSandbox()) { 172 | writeFile("hdr.h", 173 | ` 174 | typedef struct { 175 | int dwStructSize; 176 | int lpstrGroupNames; 177 | } a, *b, *c; 178 | 179 | typedef struct { 180 | int dwStructSize; 181 | int lpstrGroupNames; 182 | } x, *y, *z; 183 | 184 | #ifdef SOMETHING 185 | typedef x X; 186 | typedef y Y; 187 | typedef z Z; 188 | #else 189 | typedef a X; 190 | typedef b Y; 191 | typedef c Z; 192 | #endif 193 | `); 194 | 195 | writeFile("main.dpp", 196 | ` 197 | #include "hdr.h" 198 | void main() { } 199 | `); 200 | 201 | runPreprocessOnly("main.dpp"); 202 | 203 | shouldCompile("main.d"); 204 | } 205 | } 206 | 207 | 208 | @("check anonymous struct is defined") 209 | @safe unittest { 210 | with(immutable IncludeSandbox()) { 211 | writeFile("hdr.h", 212 | ` 213 | typedef struct { 214 | void * pad[2]; 215 | void * userContext; 216 | } * NDR_SCONTEXT; 217 | 218 | typedef struct A { 219 | NDR_SCONTEXT k; 220 | }; 221 | `); 222 | 223 | writeFile("main.dpp", 224 | ` 225 | #include "hdr.h" 226 | void main() { } 227 | `); 228 | 229 | runPreprocessOnly("main.dpp"); 230 | 231 | shouldCompile("main.d"); 232 | } 233 | } 234 | 235 | @("anon struct declaration should be missing") 236 | @safe unittest { 237 | with(immutable IncludeSandbox()) { 238 | writeFile("hdr.h", 239 | ` 240 | typedef struct { 241 | void * pad[2]; 242 | void * userContext; 243 | } * NDR_SCONTEXT; 244 | 245 | typedef struct A { 246 | NDR_SCONTEXT * k; 247 | }; 248 | 249 | void test(NDR_SCONTEXT * x); 250 | `); 251 | 252 | writeFile("main.dpp", 253 | ` 254 | #include "hdr.h" 255 | void main() { } 256 | `); 257 | 258 | runPreprocessOnly("main.dpp"); 259 | 260 | shouldCompile("main.d"); 261 | } 262 | } 263 | -------------------------------------------------------------------------------- /tests/it/c/compile/union_.d: -------------------------------------------------------------------------------- 1 | module it.c.compile.union_; 2 | 3 | 4 | import it; 5 | 6 | 7 | @("__SIZEOF_PTHREAD_ATTR_T") 8 | @safe unittest { 9 | shouldCompile( 10 | C( 11 | ` 12 | #ifdef __x86_64__ 13 | # if __WORDSIZE == 64 14 | # define __SIZEOF_PTHREAD_ATTR_T 56 15 | # else 16 | # define __SIZEOF_PTHREAD_ATTR_T 32 17 | # endif 18 | #else 19 | # define __SIZEOF_PTHREAD_ATTR_T 36 20 | #endif 21 | 22 | union pthread_attr_t 23 | { 24 | char __size[__SIZEOF_PTHREAD_ATTR_T]; 25 | long int __align; 26 | }; 27 | ` 28 | ), 29 | 30 | D( 31 | q{ 32 | pthread_attr_t attr; 33 | attr.__size[0] = 42; 34 | } 35 | ) 36 | ); 37 | } 38 | 39 | 40 | @("immediate union variable declarations") 41 | @safe unittest { 42 | shouldCompile( 43 | C( 44 | q{ 45 | struct Struct { 46 | union { 47 | int i; 48 | double d; 49 | } var1, var2; 50 | }; 51 | } 52 | ), 53 | 54 | D( 55 | q{ 56 | auto s = Struct(); 57 | static assert(is(typeof(s.var1.i) == int)); 58 | static assert(is(typeof(s.var1.d) == double)); 59 | static assert(is(typeof(s.var2.i) == int)); 60 | static assert(is(typeof(s.var2.d) == double)); 61 | } 62 | ) 63 | ); 64 | } 65 | -------------------------------------------------------------------------------- /tests/it/c/dstep/functional.d: -------------------------------------------------------------------------------- 1 | /** 2 | Tests "inspired" by the ones in dstep's functional directory 3 | */ 4 | module it.c.dstep.functional; 5 | 6 | import it; 7 | 8 | @("const int") 9 | @safe unittest { 10 | shouldCompile( 11 | C( 12 | q{ 13 | const int a; // const int 14 | const int* b; // mutable pointer to const int 15 | int* const c; // const pointer to mutable int 16 | const int* const d; // const pointer to const int 17 | const int* const * e; // mutable pointer to const pointer to const int 18 | const int* const * const f; // const pointer to const pointer to const int 19 | int* const * const g; // const pointer to const pointer to mutable int 20 | } 21 | ), 22 | D( 23 | q{ 24 | void assertType(E, A, string file = __FILE__, size_t line = __LINE__) 25 | (auto ref A t) 26 | { 27 | import std.conv: text; 28 | static assert(is(A == E), 29 | text(file, ":", line, " Expected: ", E.stringof, 30 | " Got: ", A.stringof)); 31 | } 32 | 33 | assertType!(const int)(a); 34 | assertType!(const(int)*)(b); 35 | assertType!(int*)(c); 36 | assertType!(const int*)(d); 37 | assertType!(const(int*)*)(e); 38 | assertType!(const int**)(f); 39 | assertType!(int**)(g); 40 | } 41 | ), 42 | ); 43 | } 44 | 45 | @("const struct") 46 | @safe unittest { 47 | 48 | shouldCompile( 49 | C( 50 | q{ 51 | typedef struct { int i; } Struct; 52 | const Struct a; // const Struct 53 | const Struct* b; // mutable pointer to const Struct 54 | Struct* const c; // const pointer to mutable Struct 55 | const Struct* const d; // const pointer to const Struct 56 | const Struct* const * e; // mutable pointer to const pointer to const Struct 57 | const Struct* const * const f; // const pointer to const pointer to const Struct 58 | Struct* const * const g; // const pointer to const pointer to mutable Struct 59 | } 60 | ), 61 | D( 62 | q{ 63 | void assertType(E, A, string file = __FILE__, size_t line = __LINE__) 64 | (auto ref A t) 65 | { 66 | import std.conv: text; 67 | static assert(is(A == E), 68 | text(file, ":", line, " Expected: ", E.stringof, 69 | " Got: ", A.stringof)); 70 | } 71 | 72 | assertType!(const Struct)(a); 73 | assertType!(const(Struct)*)(b); 74 | assertType!(Struct*)(c); 75 | assertType!(const Struct*)(d); 76 | assertType!(const(Struct*)*)(e); 77 | assertType!(const Struct**)(f); 78 | assertType!(Struct**)(g); 79 | } 80 | ), 81 | ); 82 | } 83 | 84 | @("dynamic") 85 | @safe unittest { 86 | shouldCompile( 87 | C( 88 | q{ 89 | typedef struct 90 | { 91 | int x; 92 | int data[0]; 93 | } Dynamic; 94 | } 95 | ), 96 | D( 97 | q{ 98 | import core.stdc.stdlib: malloc; 99 | auto d = cast(Dynamic*)malloc(Dynamic.sizeof + 5 * int.sizeof); 100 | d.x = 42; 101 | // out of bounds 102 | static assert(!__traits(compiles, d.data[3])); 103 | auto ptr = d.data.ptr; 104 | ptr[3] = 77; 105 | } 106 | ), 107 | ); 108 | } 109 | 110 | @("function_pointers") 111 | @safe unittest { 112 | shouldCompile( 113 | C( 114 | q{ 115 | void (*a) (void); 116 | int (*b) (void); 117 | void (*c) (int); 118 | int (*d) (int, int); 119 | int (*e) (int a, int b); 120 | int (*f) (int a, int b, ...); 121 | } 122 | ), 123 | D( 124 | q{ 125 | static assert(is(typeof(a()) == void)); 126 | static assert(is(typeof(b()) == int)); 127 | c(42); 128 | int dres = d(2, 3); 129 | int eres = e(4, 5); 130 | int fres = f(6, 7, 9.0, null); 131 | static assert(!__traits(compiles, f.init(6))); 132 | static assert(!__traits(compiles, f.init(6, 9.0))); 133 | } 134 | ), 135 | ); 136 | } 137 | -------------------------------------------------------------------------------- /tests/it/c/run/c.d: -------------------------------------------------------------------------------- 1 | /** 2 | C tests that must run 3 | */ 4 | module it.c.run.c; 5 | 6 | import it; 7 | 8 | @Tags("run") 9 | @("function named debug") 10 | @safe unittest { 11 | shouldCompileAndRun( 12 | C( 13 | q{ 14 | void debug(const char* msg); 15 | } 16 | ), 17 | C( 18 | ` 19 | #include 20 | void debug(const char* msg) { printf("%s\n", msg); } 21 | ` 22 | ), 23 | D( 24 | q{ 25 | debug_("Hello world!\n"); 26 | } 27 | ), 28 | ); 29 | } 30 | 31 | 32 | @Tags("run") 33 | @("struct var collision") 34 | @safe unittest { 35 | shouldCompileAndRun( 36 | C( 37 | q{ 38 | struct foo { int dummy; }; 39 | extern int foo; 40 | } 41 | ), 42 | C( 43 | q{ 44 | int foo; 45 | } 46 | ), 47 | D( 48 | q{ 49 | auto s = foo(33); 50 | foo_ = 42; 51 | } 52 | ), 53 | ); 54 | } 55 | 56 | @Tags("run") 57 | @("struct function collision") 58 | @safe unittest { 59 | shouldCompileAndRun( 60 | C( 61 | q{ 62 | struct foo { int dummy; }; 63 | void foo(void); 64 | } 65 | ), 66 | C( 67 | q{ 68 | void foo(void) {} 69 | } 70 | ), 71 | D( 72 | q{ 73 | auto s = foo(33); 74 | foo_(); 75 | } 76 | ), 77 | ); 78 | } 79 | 80 | @Tags("run") 81 | @("static.inline") 82 | @safe unittest { 83 | shouldCompileAndRun( 84 | C( 85 | ` 86 | static inline int _add(int i, int j) { 87 | return i + j; 88 | } 89 | #define add(i, j) _add(i, j) 90 | ` 91 | ), 92 | C( 93 | q{ 94 | } 95 | ), 96 | D( 97 | q{ 98 | // this is a workaround for not translating the static inline function 99 | int _add(int i, int j) { return i + j + 1; } 100 | assert(add(2, 3) == 6); 101 | } 102 | ), 103 | ); 104 | } 105 | -------------------------------------------------------------------------------- /tests/it/c/run/package.d: -------------------------------------------------------------------------------- 1 | /** 2 | Integration tests that compile and run the resulting code. 3 | */ 4 | module it.c.run; 5 | 6 | public import it; 7 | public import clang: TranslationUnit, Cursor; 8 | -------------------------------------------------------------------------------- /tests/it/c/run/struct_.d: -------------------------------------------------------------------------------- 1 | module it.c.run.struct_; 2 | 3 | import it; 4 | 5 | @Tags("run") 6 | @("structs") 7 | @safe unittest { 8 | shouldRun( 9 | C( 10 | q{ 11 | struct Foo { int i; }; 12 | 13 | struct Bar { double d; }; 14 | 15 | struct Outer { 16 | struct Inner { 17 | int x; 18 | } inner; 19 | }; 20 | 21 | typedef struct TypeDefd_ { 22 | int i; 23 | double d; 24 | } TypeDefd; 25 | 26 | typedef struct { 27 | int x, y; 28 | } Nameless1; 29 | 30 | typedef struct { 31 | double d; 32 | } Nameless2; 33 | } 34 | ), 35 | C(q{}), 36 | D( 37 | q{ 38 | auto f = Foo(5); 39 | assert(f.sizeof == 4, "Wrong sizeof for Foo"); 40 | assert(f.i == 5, "f.i should be 5"); 41 | 42 | auto b = Bar(33.3); 43 | assert(b.sizeof == 8, "Wrong sizeof for Foo"); 44 | assert(b.d == 33.3, "b.d should be 33.3"); 45 | 46 | auto o = Outer(Outer.Inner(42)); 47 | assert(o.sizeof == 4, "Wrong sizeof for Outer"); 48 | assert(o.inner.x == 42, "o.innter.x should be 42"); 49 | 50 | { 51 | auto t = TypeDefd_(42, 33.3); 52 | assert(t.sizeof == 16, "Wrong sizeof for TypeDefd_"); 53 | assert(t.i == 42, "t.i should be 42"); 54 | assert(t.d == 33.3, "t.d should be 33.3"); 55 | } 56 | { 57 | auto t = TypeDefd(42, 33.3); 58 | assert(t.sizeof == 16, "Wrong sizeof for TypeDefd"); 59 | assert(t.i == 42, "t.i should be 42"); 60 | assert(t.d == 33.3, "t.d should be 33.3"); 61 | } 62 | 63 | auto n1 = Nameless1(2, 3); 64 | assert(n1.sizeof == 8, "Wrong sizeof for Nameless1"); 65 | assert(n1.x == 2, "n1.x should be 2"); 66 | assert(n1.y == 3, "n1.y should be 3"); 67 | 68 | auto n2 = Nameless2(33.3); 69 | assert(n2.sizeof == 8, "Wrong sizeof for Nameless2"); 70 | assert(n2.d == 33.3, "n2.d should be 33.3"); 71 | } 72 | ), 73 | ); 74 | } 75 | -------------------------------------------------------------------------------- /tests/it/cpp/function_.d: -------------------------------------------------------------------------------- 1 | module it.cpp.function_; 2 | 3 | import it; 4 | 5 | @("ref basic param") 6 | unittest { 7 | shouldCompile( 8 | Cpp( 9 | q{ 10 | void fun(int& i); 11 | void gun(double& i); 12 | } 13 | ), 14 | D( 15 | q{ 16 | int i; 17 | fun(i); 18 | static assert(!__traits(compiles, fun(4))); 19 | 20 | double d; 21 | gun(d); 22 | static assert(!__traits(compiles, gun(33.3))); 23 | 24 | } 25 | ), 26 | ); 27 | } 28 | 29 | @("ref struct param") 30 | unittest { 31 | shouldCompile( 32 | Cpp( 33 | q{ 34 | struct Foo { int i; double d; }; 35 | void fun(Foo& f); 36 | } 37 | ), 38 | D( 39 | q{ 40 | auto f = Foo(2, 33.3); 41 | fun(f); 42 | static assert(!__traits(compiles, fun(Foo(2, 33.3)))); 43 | } 44 | ), 45 | ); 46 | } 47 | 48 | 49 | @("ref basic return") 50 | unittest { 51 | shouldCompile( 52 | Cpp( 53 | q{ 54 | int& fun(); 55 | double& gun(); 56 | } 57 | ), 58 | D( 59 | q{ 60 | auto i = fun(); 61 | static assert(is(typeof(i) == int), typeof(i).stringof); 62 | 63 | auto d = gun(); 64 | static assert(is(typeof(d) == double), typeof(i).stringof); 65 | } 66 | ), 67 | ); 68 | } 69 | 70 | @("ref struct return") 71 | unittest { 72 | shouldCompile( 73 | Cpp( 74 | q{ 75 | struct Foo { int i; }; 76 | Foo& fun(); 77 | } 78 | ), 79 | D( 80 | q{ 81 | auto f = fun(); 82 | static assert(is(typeof(f) == Foo), typeof(i).stringof); 83 | } 84 | ), 85 | ); 86 | } 87 | 88 | 89 | @("parameter.std.string.rename") 90 | @safe unittest { 91 | shouldCompile( 92 | Cpp( 93 | q{ 94 | namespace std { 95 | template 96 | struct basic_string {}; 97 | 98 | // naming it string clashes with D's immutable(char)[] alias 99 | using mystring = basic_string; 100 | } 101 | 102 | struct Foo { 103 | // the parameter used to get translated as 104 | // basic_string without a template parameter 105 | void fun(const std::mystring&); 106 | }; 107 | } 108 | ), 109 | D( 110 | q{ 111 | auto foo = Foo(); 112 | } 113 | ), 114 | ); 115 | } 116 | 117 | 118 | @("parameter.std.string.original.nousing") 119 | @safe unittest { 120 | shouldCompile( 121 | Cpp( 122 | q{ 123 | namespace std { 124 | template 125 | struct char_traits; 126 | 127 | template 128 | struct allocator; 129 | 130 | template , 131 | typename Allocator = allocator> 132 | struct basic_string {}; 133 | 134 | using string = basic_string; 135 | } 136 | 137 | struct String { 138 | const std::string& value(); 139 | }; 140 | 141 | void fun(const std::string&); 142 | } 143 | ), 144 | D( 145 | q{ 146 | auto str = String(); 147 | fun(str.value); 148 | auto dstr = "hello"; 149 | static assert(!__traits(compiles, fun(dstr))); 150 | } 151 | ), 152 | ); 153 | } 154 | 155 | 156 | @("parameter.std.string.original.using") 157 | @safe unittest { 158 | shouldCompile( 159 | Cpp( 160 | q{ 161 | namespace std { 162 | template 163 | struct char_traits; 164 | 165 | template 166 | struct allocator; 167 | 168 | template , 169 | typename Allocator = allocator> 170 | struct basic_string {}; 171 | 172 | using string = basic_string; 173 | } 174 | 175 | /** 176 | When this test was written, adding the using 177 | directive caused the translation to go from 178 | `string` (wrong) to `basic_string!char` (probably 179 | not what we want but at least doesn't use the alias 180 | for immutable(char)[]). 181 | */ 182 | using namespace std; 183 | 184 | struct String { 185 | const string& value(); 186 | }; 187 | 188 | void fun(const string&); 189 | } 190 | ), 191 | D( 192 | q{ 193 | auto str = String(); 194 | fun(str.value); 195 | 196 | string_ cppstr; 197 | fun(cppstr); 198 | 199 | auto dstr = "hello"; 200 | static assert(!__traits(compiles, fun(dstr))); 201 | } 202 | ), 203 | ); 204 | } 205 | 206 | 207 | 208 | @ShouldFail("Cannot currently handle templated opBinary. See dpp.translation.function_.functionDecl FIXME") 209 | @("opBinary") 210 | @safe unittest { 211 | shouldCompile( 212 | Cpp( 213 | q{ 214 | struct Foo { 215 | template 216 | int operator+(const T& other); 217 | }; 218 | } 219 | ), 220 | D( 221 | q{ 222 | auto foo = Foo(); 223 | int ret = foo + 42; 224 | } 225 | ), 226 | ); 227 | } 228 | 229 | 230 | @("opIndex") 231 | @safe unittest { 232 | shouldCompile( 233 | Cpp( 234 | q{ 235 | struct Foo { 236 | int operator[](int i); 237 | }; 238 | } 239 | ), 240 | D( 241 | q{ 242 | auto foo = Foo(); 243 | int ret = foo[42]; 244 | } 245 | ), 246 | ); 247 | } 248 | 249 | 250 | @("opOpAssign") 251 | @safe unittest { 252 | shouldCompile( 253 | Cpp( 254 | q{ 255 | struct Foo { 256 | void operator+=(int i); 257 | }; 258 | } 259 | ), 260 | D( 261 | q{ 262 | auto foo = Foo(); 263 | foo += 42; 264 | } 265 | ), 266 | ); 267 | } 268 | 269 | 270 | @("opBang") 271 | @safe unittest { 272 | shouldCompile( 273 | Cpp( 274 | q{ 275 | struct Foo { 276 | bool operator!() const; 277 | }; 278 | } 279 | ), 280 | D( 281 | q{ 282 | auto foo = Foo(); 283 | bool maybe = !foo; 284 | } 285 | ), 286 | ); 287 | } 288 | -------------------------------------------------------------------------------- /tests/it/cpp/misc.d: -------------------------------------------------------------------------------- 1 | module it.cpp.misc; 2 | 3 | 4 | import it; 5 | 6 | 7 | @("using alias") 8 | @safe unittest { 9 | shouldCompile( 10 | Cpp( 11 | q{ 12 | using foo = int; 13 | } 14 | ), 15 | D( 16 | q{ 17 | static assert(foo.sizeof == int.sizeof); 18 | foo f = 42; 19 | } 20 | ), 21 | ); 22 | } 23 | 24 | 25 | @("constexpr.braces") 26 | @safe unittest { 27 | shouldCompile( 28 | Cpp( 29 | q{ 30 | constexpr int var{}; 31 | } 32 | ), 33 | D( 34 | q{ 35 | static assert(is(typeof(var) == const(int))); 36 | } 37 | ), 38 | ); 39 | } 40 | 41 | 42 | @("enum.class.decl") 43 | @safe unittest { 44 | shouldCompile( 45 | Cpp( 46 | q{ 47 | enum class byte : unsigned char; 48 | } 49 | ), 50 | D( 51 | q{ 52 | } 53 | ), 54 | ); 55 | } 56 | 57 | @("namespaceless") 58 | @safe unittest { 59 | shouldCompile( 60 | Cpp( 61 | q{ 62 | namespace ns { 63 | class C1 { 64 | public: 65 | 66 | class C2; 67 | }; 68 | } 69 | 70 | using C1_Hidden = ns::C1; 71 | 72 | namespace ns { 73 | using _C2 = ::C1_Hidden::C2; 74 | } 75 | } 76 | ), 77 | D( 78 | q{ 79 | } 80 | ), 81 | [], 82 | ); 83 | } 84 | -------------------------------------------------------------------------------- /tests/it/docs.d: -------------------------------------------------------------------------------- 1 | module it.docs; 2 | 3 | import it; 4 | import dpp.translation; 5 | import unit_threaded: shouldEqual, Sandbox; 6 | 7 | import std.file: readText; 8 | import std.string: indexOf; 9 | 10 | import unit_threaded.assertions: shouldBeIn; 11 | 12 | auto normalizeLines(string text) { 13 | import std.string: strip, lineSplitter; 14 | import std.algorithm: map; 15 | import std.array: join; 16 | return text.lineSplitter().map!(line => line.strip()).join('\n'); 17 | } 18 | 19 | 20 | version(Windows) { 21 | @("The Cpp documentation is preserved") 22 | @safe unittest { 23 | with(immutable IncludeSandbox()) { 24 | 25 | writeFile("hdr.hpp", 26 | ` 27 | /** f1 doc */ 28 | void f1() {} 29 | 30 | // f2 non-doc 31 | void f2() {} 32 | 33 | /// f3 doc 34 | void f3() {} 35 | 36 | /** variable1 doc */ 37 | int variable1 = 1 + 2; 38 | 39 | /// variable2 doc 40 | int variable2 = 1 + 2; 41 | 42 | // variable3 non-doc 43 | int variable3 = 1 + 2; 44 | 45 | 46 | /** Struct1 doc */ 47 | struct Struct1 { 48 | 49 | /** prop1 doc */ 50 | int prop1; 51 | 52 | /** method1 doc */ 53 | void method1(); 54 | }; 55 | 56 | 57 | /** Enum1 doc */ 58 | enum Enum1 { 59 | /** x doc */ 60 | x, 61 | /// y doc 62 | y, 63 | // z non doc 64 | z, 65 | }; 66 | 67 | /** Type1 doc */ 68 | typedef int Type1;`); 69 | writeFile("main.dpp", 70 | ` 71 | #include "hdr.hpp" 72 | `); 73 | runPreprocessOnly( 74 | "--source-output-path", 75 | inSandboxPath("foo/bar"), 76 | "main.dpp", 77 | ); 78 | 79 | auto originalText = readText(inSandboxPath("foo/bar/main.d")); 80 | auto needle = `extern(C++) 81 | { 82 | /** Type1 doc */ 83 | alias Type1 = int; 84 | /** Enum1 doc */ 85 | enum Enum1 86 | { 87 | /** x doc */ 88 | x = 0, 89 | /// y doc 90 | y = 1, 91 | /// y doc 92 | z = 2, 93 | } 94 | enum x = Enum1.x; 95 | enum y = Enum1.y; 96 | enum z = Enum1.z; 97 | /** Struct1 doc */ 98 | struct Struct1 99 | { 100 | /** prop1 doc */ 101 | int prop1; 102 | /** method1 doc */ 103 | pragma(mangle, "?method1@Struct1@@QEAAXXZ") void method1() @nogc nothrow; 104 | } 105 | 106 | pragma(mangle, "?variable3@@3HA") extern export __gshared int variable3; 107 | /// variable2 doc 108 | pragma(mangle, "?variable2@@3HA") extern export __gshared int variable2; 109 | /** variable1 doc */ 110 | pragma(mangle, "?variable1@@3HA") extern export __gshared int variable1; 111 | /// f3 doc 112 | pragma(mangle, "?f3@@YAXXZ") void f3() @nogc nothrow; 113 | 114 | pragma(mangle, "?f2@@YAXXZ") void f2() @nogc nothrow; 115 | /** f1 doc */ 116 | pragma(mangle, "?f1@@YAXXZ") void f1() @nogc nothrow; 117 | }`; 118 | needle.normalizeLines().shouldBeIn(originalText[originalText.indexOf("extern(C++)")..$].normalizeLines()); 119 | 120 | } 121 | } 122 | } 123 | 124 | @("The C documentation is preserved") 125 | @safe unittest { 126 | with(immutable IncludeSandbox()) { 127 | writeFile("hdr.h", 128 | ` 129 | /** f1 doc */ 130 | void f1() {} 131 | 132 | // f2 non-doc 133 | void f2() {} 134 | 135 | /// f3 doc 136 | void f3() {} 137 | 138 | /** variable1 doc */ 139 | int variable1 = 1 + 2; 140 | 141 | /// variable2 doc 142 | int variable2 = 1 + 2; 143 | 144 | // variable3 non-doc 145 | int variable3 = 1 + 2; 146 | 147 | 148 | /** Struct1 doc */ 149 | struct Struct1 { 150 | 151 | /** prop1 doc */ 152 | int prop1; 153 | }; 154 | 155 | 156 | /** Enum1 doc */ 157 | enum Enum1 { 158 | /** x doc */ 159 | x, 160 | /// y doc 161 | y, 162 | // z non doc 163 | z, 164 | }; 165 | 166 | /** Type1 doc */ 167 | typedef int Type1; 168 | `); 169 | writeFile("main.dpp", 170 | ` 171 | #include "hdr.h" 172 | `); 173 | runPreprocessOnly( 174 | "--source-output-path", 175 | inSandboxPath("foo/bar"), 176 | "main.dpp", 177 | ); 178 | 179 | auto originalText = readText(inSandboxPath("foo/bar/main.d")); 180 | auto needle = `extern(C) 181 | { 182 | /** Type1 doc */ 183 | alias Type1 = int; 184 | /** Enum1 doc */ 185 | enum Enum1 186 | { 187 | /** x doc */ 188 | x = 0, 189 | /// y doc 190 | y = 1, 191 | /// y doc 192 | z = 2, 193 | } 194 | enum x = Enum1.x; 195 | enum y = Enum1.y; 196 | enum z = Enum1.z; 197 | /** Struct1 doc */ 198 | struct Struct1 199 | { 200 | /** prop1 doc */ 201 | int prop1; 202 | } 203 | 204 | extern export __gshared int variable3; 205 | /// variable2 doc 206 | extern export __gshared int variable2; 207 | /** variable1 doc */ 208 | extern export __gshared int variable1; 209 | /// f3 doc 210 | void f3() @nogc nothrow; 211 | 212 | void f2() @nogc nothrow; 213 | /** f1 doc */ 214 | void f1() @nogc nothrow; 215 | }`; 216 | needle.normalizeLines().shouldBeIn(originalText[originalText.indexOf("extern(C)")..$].normalizeLines()); 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /tests/it/expansion.d: -------------------------------------------------------------------------------- 1 | module it.expansion; 2 | 3 | 4 | import it; 5 | import common: printChildren, shouldMatch; 6 | import dpp.expansion; 7 | import clang: parse, TranslationUnit, Cursor, Type; 8 | import std.conv: text; 9 | import std.array: join; 10 | import std.algorithm: map; 11 | 12 | 13 | @("namespace") 14 | @safe unittest { 15 | 16 | 17 | auto tu = () { 18 | 19 | with(immutable IncludeSandbox()) { 20 | writeFile( 21 | "foo.cpp", 22 | q{ 23 | int globalInt; 24 | 25 | namespace outer1 { 26 | namespace inner1 { 27 | struct Foo; 28 | struct Bar; 29 | 30 | struct Baz { 31 | int i; 32 | double d; 33 | }; 34 | } 35 | } 36 | 37 | namespace outer2 { 38 | struct Quux { }; 39 | } 40 | 41 | namespace outer1 { 42 | namespace inner1 { 43 | struct Foo { 44 | int i; 45 | }; 46 | 47 | struct Bar { 48 | double d; 49 | }; 50 | } 51 | } 52 | }); 53 | 54 | return parse(inSandboxPath("foo.cpp")); 55 | } 56 | }(); 57 | 58 | const cursors = canonicalCursors(tu); 59 | writelnUt(cursors.map!text.join("\n")); 60 | cursors.length.should == 3; 61 | 62 | const globalInt = cursors[0]; 63 | globalInt.shouldMatch(Cursor.Kind.VarDecl, "globalInt"); 64 | 65 | const outer1 = cursors[1]; 66 | outer1.shouldMatch(Cursor.Kind.Namespace, "outer1"); 67 | printChildren(outer1); 68 | outer1.children.length.should == 1; 69 | 70 | const inner1 = outer1.children[0]; 71 | inner1.shouldMatch(Cursor.Kind.Namespace, "inner1"); 72 | printChildren(inner1); 73 | inner1.children.length.should == 3; 74 | inner1.children.map!(a => a.spelling).shouldBeSameSetAs(["Foo", "Bar", "Baz"]); 75 | 76 | const outer2 = cursors[2]; 77 | outer2.shouldMatch(Cursor.Kind.Namespace, "outer2"); 78 | printChildren(outer2); 79 | outer2.children.length.should == 1; 80 | 81 | const quux = outer2.children[0]; 82 | quux.shouldMatch(Cursor.Kind.StructDecl, "Quux"); 83 | } 84 | 85 | 86 | @("issue113") 87 | @safe unittest { 88 | 89 | auto tu = () { 90 | with(immutable Sandbox()) { 91 | writeFile( 92 | "foo.cpp", 93 | q{ 94 | namespace ns1 { 95 | namespace ns2 { 96 | struct Struct; 97 | 98 | template 99 | struct Template { 100 | }; 101 | 102 | class Class; // should be ignored but isn't 103 | class Class: public Template { 104 | int i; 105 | }; 106 | } 107 | } 108 | }); 109 | 110 | return parse(inSandboxPath("foo.cpp")); 111 | } 112 | }(); 113 | 114 | const cursors = canonicalCursors(tu); 115 | writelnUt(cursors.map!text.join("\n")); 116 | cursors.length.should == 1; 117 | 118 | const ns1 = cursors[0]; 119 | ns1.shouldMatch(Cursor.Kind.Namespace, "ns1"); 120 | printChildren(ns1); 121 | ns1.children.length.should == 1; 122 | 123 | const ns2 = ns1.children[0]; 124 | ns2.shouldMatch(Cursor.Kind.Namespace, "ns2"); 125 | printChildren(ns2); 126 | ns2.children.map!(a => a.spelling).shouldBeSameSetAs(["Struct", "Template", "Class"]); 127 | ns2.children.length.should == 3; 128 | } 129 | -------------------------------------------------------------------------------- /tests/it/main.d: -------------------------------------------------------------------------------- 1 | import unit_threaded.runner; 2 | 3 | 4 | mixin runTestsMain!( 5 | "it.issues", 6 | "it.expansion", 7 | "it.docs", 8 | 9 | // C tests 10 | "it.c.compile.preprocessor", 11 | "it.c.compile.struct_", 12 | "it.c.compile.union_", 13 | "it.c.compile.array", 14 | "it.c.compile.enum_", 15 | "it.c.compile.typedef_", 16 | "it.c.compile.function_", 17 | "it.c.compile.projects", 18 | "it.c.compile.runtime_args", 19 | "it.c.compile.collision", 20 | "it.c.compile.extensions", 21 | "it.c.run.struct_", 22 | "it.c.run.c", 23 | 24 | // tests copied from dstep 25 | "it.c.dstep.ut", 26 | "it.c.dstep.functional", 27 | "it.c.dstep.issues", 28 | 29 | // C++ tests 30 | "it.cpp.run", 31 | "it.cpp.function_", 32 | "it.cpp.class_", 33 | "it.cpp.templates", 34 | "it.cpp.misc", 35 | "it.cpp.opaque", 36 | ); 37 | -------------------------------------------------------------------------------- /tests/test_main.d: -------------------------------------------------------------------------------- 1 | module tests.test_main; 2 | 3 | import unit_threaded.runner.runner; 4 | 5 | version(dpp2) { 6 | mixin runTestsMain!( 7 | // unit tests 8 | 9 | "ut.translation.type.primitives", 10 | "ut.translation.type.array", 11 | "ut.translation.type.pointer", 12 | 13 | "ut.translation.node.structs", 14 | 15 | "ut.transform.clang", 16 | "ut.transform.cursor", 17 | "ut.transform.type", 18 | 19 | // integration tests 20 | "it.c.compile.struct_", 21 | ); 22 | } else { 23 | mixin runTestsMain!( 24 | // in-file 25 | "dpp.runtime", 26 | "dpp.translation", 27 | "dpp.translation.macro_", 28 | "dpp.expansion", 29 | 30 | // unit tests 31 | "ut.old.type", 32 | "ut.expansion", 33 | 34 | // contract tests 35 | "contract.array", 36 | "contract.templates", 37 | "contract.namespace", 38 | "contract.macro_", 39 | "contract.constexpr", 40 | "contract.typedef_", 41 | "contract.operators", 42 | "contract.member", 43 | "contract.aggregates", 44 | "contract.inheritance", 45 | "contract.issues", 46 | "contract.methods", 47 | "contract.functions", 48 | "contract.enums", 49 | 50 | "it.issues", 51 | "it.expansion", 52 | "it.docs", 53 | 54 | // C tests 55 | "it.c.compile.preprocessor", 56 | "it.c.compile.struct_", 57 | "it.c.compile.union_", 58 | "it.c.compile.array", 59 | "it.c.compile.enum_", 60 | "it.c.compile.typedef_", 61 | "it.c.compile.function_", 62 | "it.c.compile.projects", 63 | "it.c.compile.runtime_args", 64 | "it.c.compile.collision", 65 | "it.c.compile.extensions", 66 | "it.c.run.struct_", 67 | "it.c.run.c", 68 | 69 | // tests copied from dstep 70 | "it.c.dstep.ut", 71 | "it.c.dstep.functional", 72 | "it.c.dstep.issues", 73 | 74 | // C++ tests 75 | "it.cpp.run", 76 | "it.cpp.function_", 77 | "it.cpp.class_", 78 | "it.cpp.templates", 79 | "it.cpp.misc", 80 | "it.cpp.opaque", 81 | ); 82 | } 83 | -------------------------------------------------------------------------------- /tests/ut/expansion.d: -------------------------------------------------------------------------------- 1 | module ut.expansion; 2 | 3 | import ut; 4 | import dpp.expansion; 5 | 6 | 7 | /// 8 | @("getHeaderName") 9 | @safe pure unittest { 10 | import unit_threaded: shouldEqual; 11 | getHeaderName(`#include "foo.h"`).shouldEqual(`foo.h`); 12 | getHeaderName(`#include "bar.h"`).shouldEqual(`bar.h`); 13 | getHeaderName(`#include "foo.h" // comment`).shouldEqual(`foo.h`); 14 | getHeaderName(`#include `).shouldEqual(`foo.h`); 15 | getHeaderName(` #include "foo.h"`).shouldEqual(`foo.h`); 16 | } 17 | -------------------------------------------------------------------------------- /tests/ut/old/type.d: -------------------------------------------------------------------------------- 1 | module ut.old.type; 2 | 3 | 4 | import dpp.from; 5 | import dpp.translation.type; 6 | import clang: Type; 7 | import unit_threaded; 8 | 9 | 10 | string translate(in from!"clang".Type type) @safe { 11 | import dpp.translation.type: translate_ = translate; 12 | import dpp.runtime.context: Context; 13 | Context context; 14 | return translate_(type, context); 15 | } 16 | 17 | @("void") 18 | @safe unittest { 19 | Type(Type.Kind.Void).translate.shouldEqual("void"); 20 | } 21 | 22 | @("bool") 23 | @safe unittest { 24 | Type(Type.Kind.Bool).translate.shouldEqual("bool"); 25 | } 26 | 27 | @("char_u") 28 | @safe unittest { 29 | Type(Type.Kind.Char_U).translate.shouldEqual("ubyte"); 30 | } 31 | 32 | @("UChar") 33 | @safe unittest { 34 | Type(Type.Kind.UChar).translate.shouldEqual("ubyte"); 35 | } 36 | 37 | @("Char16") 38 | @safe unittest { 39 | Type(Type.Kind.Char16).translate.shouldEqual("wchar"); 40 | } 41 | 42 | @("Char32") 43 | @safe unittest { 44 | Type(Type.Kind.Char32).translate.shouldEqual("dchar"); 45 | } 46 | 47 | @("unsigned short") 48 | @safe unittest { 49 | Type(Type.Kind.UShort).translate.shouldEqual("ushort"); 50 | } 51 | 52 | @("unsigned int") 53 | @safe unittest { 54 | Type(Type.Kind.UInt).translate.shouldEqual("uint"); 55 | } 56 | 57 | @("unsigned long") 58 | @safe unittest { 59 | Type(Type.Kind.ULong).translate.shouldEqual("c_ulong"); 60 | } 61 | 62 | @("unsigned long long") 63 | @safe unittest { 64 | Type(Type.Kind.ULongLong).translate.shouldEqual("ulong"); 65 | } 66 | 67 | @("uint128") 68 | @safe unittest { 69 | Type(Type.Kind.UInt128).translate.shouldEqual("UInt128"); 70 | } 71 | 72 | @("char_s") 73 | @safe unittest { 74 | Type(Type.Kind.Char_S).translate.shouldEqual("char"); 75 | } 76 | 77 | @("SChar") 78 | @safe unittest { 79 | Type(Type.Kind.SChar).translate.shouldEqual("byte"); 80 | } 81 | 82 | @("WChar") 83 | @safe unittest { 84 | Type(Type.Kind.WChar).translate.shouldEqual("wchar"); 85 | } 86 | 87 | @("short") 88 | @safe unittest { 89 | Type(Type.Kind.Short).translate.shouldEqual("short"); 90 | } 91 | 92 | @("int") 93 | @safe unittest { 94 | Type(Type.Kind.Int).translate.shouldEqual("int"); 95 | } 96 | 97 | @("long") 98 | @safe unittest { 99 | Type(Type.Kind.Long).translate.shouldEqual("c_long"); 100 | } 101 | 102 | @("long long") 103 | @safe unittest { 104 | Type(Type.Kind.LongLong).translate.shouldEqual("long"); 105 | } 106 | 107 | @("int128") 108 | @safe unittest { 109 | Type(Type.Kind.Int128).translate.shouldEqual("Int128"); 110 | } 111 | 112 | @("float") 113 | @safe unittest { 114 | Type(Type.Kind.Float).translate.shouldEqual("float"); 115 | } 116 | 117 | @("double") 118 | @safe unittest { 119 | Type(Type.Kind.Double).translate.shouldEqual("double"); 120 | } 121 | 122 | @("long double") 123 | @safe unittest { 124 | Type(Type.Kind.LongDouble).translate.shouldEqual("real"); 125 | } 126 | 127 | @("nullptr") 128 | @safe unittest { 129 | Type(Type.Kind.NullPtr).translate.shouldEqual("void*"); 130 | } 131 | 132 | @("float128") 133 | @safe unittest { 134 | Type(Type.Kind.Float128).translate.shouldEqual("real"); 135 | } 136 | 137 | @("half") 138 | @safe unittest { 139 | Type(Type.Kind.Half).translate.shouldEqual("float"); 140 | } 141 | -------------------------------------------------------------------------------- /tests/ut/package.d: -------------------------------------------------------------------------------- 1 | module ut; 2 | 3 | public import unit_threaded; 4 | public import std.conv: text; 5 | --------------------------------------------------------------------------------