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 }