1 /**
2 Copyright: Copyright (c) 2017, Joakim Brännström. All rights reserved.
3 License: MPL-2
4 Author: Joakim Brännström (joakim.brannstrom@gmx.com)
5 
6 This Source Code Form is subject to the terms of the Mozilla Public License,
7 v.2.0. If a copy of the MPL was not distributed with this file, You can obtain
8 one at http://mozilla.org/MPL/2.0/.
9 */
10 module dextool.plugin.fuzzer.backend.analyzer;
11 
12 import logger = std.experimental.logger;
13 
14 import libclang_ast.ast : Visitor;
15 import cpptooling.data : CppRoot, CppClass, CppMethod, CppCtor, CppDtor,
16     CFunction, CppNamespace, USRType, Language, LocationTag, Location;
17 
18 import dextool.type : Path;
19 
20 import dextool.plugin.fuzzer.backend.interface_;
21 
22 /// Data derived during analyze of one translation unit.
23 struct AnalyzeData {
24     static auto make() {
25         auto r = AnalyzeData(CppRoot.make);
26         return r;
27     }
28 
29     CppRoot root;
30     alias root this;
31 
32     /// Either all functions have the same or it is a mix which result in unknown.
33     Language languageOfTranslationUnit;
34 
35     Path fileOfTranslationUnit;
36 }
37 
38 private enum LanguageAnalyzeState {
39     none,
40     unknown,
41     oneLanguage,
42     mixed
43 }
44 
45 private LanguageAnalyzeState mergeLanguage(LanguageAnalyzeState st,
46         Language func_lang, ref Language current_lang) @safe pure nothrow @nogc {
47     import std.algorithm : among;
48 
49     final switch (st) {
50     case LanguageAnalyzeState.none:
51         if (func_lang.among(Language.c, Language.cpp)) {
52             st = LanguageAnalyzeState.oneLanguage;
53         } else {
54             st = LanguageAnalyzeState.unknown;
55         }
56         break;
57     case LanguageAnalyzeState.unknown:
58         break;
59     case LanguageAnalyzeState.oneLanguage:
60         if (func_lang != current_lang) {
61             st = LanguageAnalyzeState.mixed;
62         }
63         break;
64     case LanguageAnalyzeState.mixed:
65         break;
66     }
67 
68     final switch (st) {
69     case LanguageAnalyzeState.none:
70         break;
71     case LanguageAnalyzeState.unknown:
72         current_lang = Language.unknown;
73         break;
74     case LanguageAnalyzeState.oneLanguage:
75         current_lang = func_lang;
76         break;
77     case LanguageAnalyzeState.mixed:
78         current_lang = Language.unknown;
79         break;
80     }
81 
82     return st;
83 }
84 
85 final class TUVisitor : Visitor {
86     import std.typecons : scoped, NullableRef;
87 
88     import libclang_ast.ast : UnexposedDecl, FunctionDecl, TranslationUnit,
89         generateIndentIncrDecr, LinkageSpec;
90     import cpptooling.analyzer.clang.analyze_helper : analyzeFunctionDecl;
91     import cpptooling.data : CxReturnType;
92     import cpptooling.data.symbol : Container;
93     import libclang_ast.cursor_logger : logNode, mixinNodeLog;
94 
95     alias visit = Visitor.visit;
96 
97     mixin generateIndentIncrDecr;
98 
99     NullableRef!Container container;
100     AnalyzeData root;
101 
102     private LanguageAnalyzeState lang_analyze_st;
103 
104     this(NullableRef!Container container) {
105         this.container = container;
106         this.root = AnalyzeData.make;
107     }
108 
109     override void visit(scope const UnexposedDecl v) {
110         mixin(mixinNodeLog!());
111         v.accept(this);
112     }
113 
114     override void visit(scope const LinkageSpec v) {
115         mixin(mixinNodeLog!());
116         // extern "C"... etc
117         v.accept(this);
118     }
119 
120     override void visit(scope const FunctionDecl v) {
121         mixin(mixinNodeLog!());
122 
123         auto result = analyzeFunctionDecl(v, container, indent);
124         if (!result.isValid) {
125             return;
126         }
127 
128         auto func = CFunction(result.type.kind.usr, result.name, result.params,
129                 CxReturnType(result.returnType), result.isVariadic, result.storageClass);
130         func.language = result.language;
131         root.put(func);
132 
133         mergeLanguage(lang_analyze_st, result.language, root.languageOfTranslationUnit);
134 
135         debug logger.tracef("FunctionDecl, language: %s tu(%s)",
136                 result.language, root.languageOfTranslationUnit);
137     }
138 
139     override void visit(scope const TranslationUnit v) {
140         mixin(mixinNodeLog!());
141 
142         root.fileOfTranslationUnit = Path(v.cursor.spelling);
143 
144         v.accept(this);
145     }
146 }