1 /** 2 Date: 2015-2016, Joakim Brännström 3 License: MPL-2, Mozilla Public License 2.0 4 Author: Joakim Brännström (joakim.brannstrom@gmx.com) 5 */ 6 module cpptooling.analyzer.clang.context; 7 8 import std.typecons : Flag; 9 import logger = std.experimental.logger; 10 11 version (unittest) { 12 import unit_threaded : Name, shouldEqual; 13 } 14 15 @safe: 16 17 /** Convenient context of items needed to practically create a clang AST. 18 * 19 * "Creating a clang AST" means calling $(D makeTranslationUnit). 20 * 21 * Assumes that the scope of a ClangContext is the same as that stated for a 22 * clang Index. Namely that those translation units that are created are the 23 * same as those that would be linked together into an executable or library. 24 * 25 * Items are: 26 * - An index that all translation units use as input. 27 * - A VFS providing access to the files that the translation unites are 28 * derived from. 29 */ 30 struct ClangContext { 31 import clang.Index : Index; 32 import clang.TranslationUnit : TranslationUnit; 33 34 import blob_model : BlobVfs, Uri, Blob; 35 36 import clang.c.Index : CXTranslationUnit_Flags; 37 38 private { 39 Index index; 40 string[] internal_header_arg; 41 string[] syntax_only_arg; 42 } 43 44 /** Access to the virtual filesystem used when instantiating translation 45 * units. 46 */ 47 BlobVfs vfs; 48 49 @disable this(); 50 51 // The context is "heavy" therefor disabling moving. 52 @disable this(this); 53 54 /** Create an instance. 55 * 56 * The binary dextool has clang specified headers attached. Those are feed 57 * to the VFS and used when the flag useInternalHeaders is "yes". To make 58 * them accessable a "-I" parameter with their in-memory location is 59 * supplied to all instantiated translation units. 60 * 61 * TODO from llvm-6.0 -fsyntax-only is default and ignored. The 62 * functionality to prepend with -fsyntax-only should thus be removed. 63 * 64 * Params: 65 * useInternalHeaders = load the VFS with in-memory system headers. 66 * prependParamSyntaxOnly = prepend the flag -fsyntax-only to instantiated translation units. 67 */ 68 this(Flag!"useInternalHeaders" useInternalHeaders, 69 Flag!"prependParamSyntaxOnly" prependParamSyntaxOnly) @trusted { 70 this.index = Index(false, false); 71 this.vfs = new BlobVfs; 72 73 if (useInternalHeaders) { 74 import cpptooling.utility.virtualfilesystem : FileName; 75 import clang.Compiler : Compiler; 76 77 Compiler compiler; 78 this.internal_header_arg = compiler.extraIncludeFlags; 79 foreach (hdr; compiler.extraHeaders) { 80 auto f = vfs.open(new Blob(Uri(hdr.filename), hdr.content)); 81 } 82 } 83 84 if (prependParamSyntaxOnly) { 85 this.syntax_only_arg = ["-fsyntax-only"]; 86 } 87 } 88 89 /** Create a translation unit from the context. 90 * 91 * The translation unit is NOT kept by the context. 92 */ 93 auto makeTranslationUnit(in string sourceFilename, in string[] commandLineArgs = null, 94 uint options = CXTranslationUnit_Flags.detailedPreprocessingRecord) @safe { 95 import std.array : join; 96 97 auto prependDefaultFlags(string[] in_cflags) { 98 import std.algorithm : canFind; 99 100 if (in_cflags.canFind(syntax_only_arg)) { 101 return in_cflags; 102 } else { 103 return syntax_only_arg ~ in_cflags; 104 } 105 } 106 107 debug logger.trace(sourceFilename); 108 debug logger.trace("Compiler flags: ", commandLineArgs.join(" ")); 109 110 string[] args = prependDefaultFlags(commandLineArgs ~ internal_header_arg); 111 112 debug logger.trace("Internal compiler flags: ", args.join(" ")); 113 114 const uri = Uri(sourceFilename); 115 116 // ensure the file exist in the filesys layer. 117 // it has either been added as an in-memory file by the user or it is 118 // read from the filesystem. 119 if (!vfs.exists(uri)) { 120 vfs.openFromFile(uri); 121 } 122 123 import cpptooling.utility.virtualfilesystem : toClangFiles; 124 125 auto files = vfs.toClangFiles; 126 127 return TranslationUnit.parse(index, sourceFilename, args, files); 128 } 129 } 130 131 @("shall be an instance") 132 @system unittest { 133 import std.typecons : Yes; 134 135 auto ctx = ClangContext(Yes.useInternalHeaders, Yes.prependParamSyntaxOnly); 136 }