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     CppRoot root;
25     alias root this;
26 
27     /// Either all functions have the same or it is a mix which result in unknown.
28     Language languageOfTranslationUnit;
29 
30     Path fileOfTranslationUnit;
31 }
32 
33 private enum LanguageAnalyzeState {
34     none,
35     unknown,
36     oneLanguage,
37     mixed
38 }
39 
40 private LanguageAnalyzeState mergeLanguage(LanguageAnalyzeState st,
41         Language func_lang, ref Language current_lang) @safe pure nothrow @nogc {
42     import std.algorithm : among;
43 
44     final switch (st) {
45     case LanguageAnalyzeState.none:
46         if (func_lang.among(Language.c, Language.cpp)) {
47             st = LanguageAnalyzeState.oneLanguage;
48         } else {
49             st = LanguageAnalyzeState.unknown;
50         }
51         break;
52     case LanguageAnalyzeState.unknown:
53         break;
54     case LanguageAnalyzeState.oneLanguage:
55         if (func_lang != current_lang) {
56             st = LanguageAnalyzeState.mixed;
57         }
58         break;
59     case LanguageAnalyzeState.mixed:
60         break;
61     }
62 
63     final switch (st) {
64     case LanguageAnalyzeState.none:
65         break;
66     case LanguageAnalyzeState.unknown:
67         current_lang = Language.unknown;
68         break;
69     case LanguageAnalyzeState.oneLanguage:
70         current_lang = func_lang;
71         break;
72     case LanguageAnalyzeState.mixed:
73         current_lang = Language.unknown;
74         break;
75     }
76 
77     return st;
78 }
79 
80 final class TUVisitor : Visitor {
81     import std.typecons : scoped, NullableRef;
82 
83     import libclang_ast.ast : UnexposedDecl, FunctionDecl, TranslationUnit,
84         generateIndentIncrDecr, LinkageSpec;
85     import cpptooling.analyzer.clang.analyze_helper : analyzeFunctionDecl;
86     import cpptooling.data : CxReturnType;
87     import cpptooling.data.symbol : Container;
88     import libclang_ast.cursor_logger : logNode, mixinNodeLog;
89 
90     alias visit = Visitor.visit;
91 
92     mixin generateIndentIncrDecr;
93 
94     NullableRef!Container container;
95     AnalyzeData root;
96 
97     private LanguageAnalyzeState lang_analyze_st;
98 
99     this(NullableRef!Container container) {
100         this.container = container;
101     }
102 
103     override void visit(scope const UnexposedDecl v) {
104         mixin(mixinNodeLog!());
105         v.accept(this);
106     }
107 
108     override void visit(scope const LinkageSpec v) {
109         mixin(mixinNodeLog!());
110         // extern "C"... etc
111         v.accept(this);
112     }
113 
114     override void visit(scope const FunctionDecl v) {
115         mixin(mixinNodeLog!());
116 
117         auto result = analyzeFunctionDecl(v, container, indent);
118         if (!result.isValid) {
119             return;
120         }
121 
122         auto func = CFunction(result.type.kind.usr, result.name, result.params,
123                 CxReturnType(result.returnType), result.isVariadic, result.storageClass);
124         func.language = result.language;
125         root.put(func);
126 
127         mergeLanguage(lang_analyze_st, result.language, root.languageOfTranslationUnit);
128 
129         debug logger.tracef("FunctionDecl, language: %s tu(%s)",
130                 result.language, root.languageOfTranslationUnit);
131     }
132 
133     override void visit(scope const TranslationUnit v) {
134         mixin(mixinNodeLog!());
135 
136         root.fileOfTranslationUnit = Path(v.cursor.spelling);
137 
138         v.accept(this);
139     }
140 }