1 | /**************************************************************************** |
2 | * Copyright (C) 2013-2016 Woboq GmbH |
3 | * Olivier Goffart <contact at woboq.com> |
4 | * https://woboq.com/ |
5 | * |
6 | * This program is free software: you can redistribute it and/or modify |
7 | * it under the terms of the GNU General Public License as published by |
8 | * the Free Software Foundation, either version 3 of the License, or |
9 | * (at your option) any later version. |
10 | * |
11 | * This program is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | * GNU General Public License for more details. |
15 | * |
16 | * You should have received a copy of the GNU General Public License |
17 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
18 | */ |
19 | |
20 | #include "mocppcallbacks.h" |
21 | #include "mocastconsumer.h" |
22 | #include "clangversionabstraction.h" |
23 | #include <clang/AST/ASTContext.h> |
24 | #include <clang/AST/DeclCXX.h> |
25 | #include <clang/AST/DeclTemplate.h> |
26 | #include <clang/Sema/Sema.h> |
27 | #include <clang/Basic/MacroBuilder.h> |
28 | |
29 | |
30 | void MocASTConsumer::Initialize(clang::ASTContext& Ctx) { |
31 | ctx = &Ctx; |
32 | PPCallbacks = new MocPPCallbacks(ci.getPreprocessor(), Moc.Tags); |
33 | ci.getPreprocessor().addPPCallbacks(maybe_unique(PPCallbacks)); |
34 | // ci.getDiagnostics().setClient(new DiagnosticClient(), true); |
35 | |
36 | // We will enable this when we require Qt >= 5.6.1 and libclang >= 3.8 |
37 | // Then we will be able to get rid of qobjectdefs-injected |
38 | #if 0 |
39 | std::string qtPredefinesBuffer; |
40 | llvm::raw_string_ostream qtPredefines(qtPredefinesBuffer); |
41 | clang::MacroBuilder builder(qtPredefines); |
42 | builder.append("# 1 \"<moc-ng built-in>\" 1" ); |
43 | builder.defineMacro("QT_ANNOTATE_CLASS(type,...)" , "static_assert(sizeof(#__VA_ARGS__),#type);" ); |
44 | builder.defineMacro("QT_ANNOTATE_CLASS2(type,a1,a2)" , "static_assert(sizeof(#a1,#a2),#type);" ); |
45 | builder.defineMacro("QT_ANNOTATE_FUNCTION(a)" , "__attribute__((annotate(#a)))" ); |
46 | builder.defineMacro("QT_ANNOTATE_ACCESS_SPECIFIER(a)" , "__attribute__((annotate(#a)))" ); |
47 | builder.defineMacro("Q_CLASSINFO(name,value)" , "static_assert(sizeof(name,value),\"qt_classinfo\");" ); |
48 | builder.defineMacro("Q_REVISION(v)" , "__attribute__((annotate(\"qt_revision:\" QT_STRINGIFY2(v))))" ); |
49 | // prepend the Qt defines so the command line argument can override them. |
50 | ci.getPreprocessor().setPredefines(qtPredefines.str() + ci.getPreprocessor().getPredefines()); |
51 | #endif |
52 | } |
53 | |
54 | |
55 | void MocASTConsumer::HandleTagDeclDefinition(clang::TagDecl* D) |
56 | { |
57 | clang::CXXRecordDecl *RD = llvm::dyn_cast<clang::CXXRecordDecl>(D); |
58 | if (!RD) |
59 | return; |
60 | |
61 | if (!shouldParseDecl(D)) |
62 | return; |
63 | |
64 | clang::ClassTemplateSpecializationDecl* TD = llvm::dyn_cast<clang::ClassTemplateSpecializationDecl>(RD); |
65 | if (TD && TD->getIdentifier() && TD->getName() == "QMetaTypeId" && TD->getTemplateArgs().size() == 1) { |
66 | Moc.registered_meta_type.insert(TD->getTemplateArgs().get(0).getAsType()->getCanonicalTypeUnqualified().getTypePtr()); |
67 | } |
68 | |
69 | if (TD) { |
70 | // Do not parse class for class template specialization |
71 | return; |
72 | } |
73 | |
74 | |
75 | /*if (!(PPCallbacks->seenQ_OBJECT.isValid() && |
76 | ctx->getSourceManager().isBeforeInTranslationUnit(D->getSourceRange().getBegin(), |
77 | PPCallbacks->seenQ_OBJECT) && |
78 | ctx->getSourceManager().isBeforeInTranslationUnit(PPCallbacks->seenQ_OBJECT, |
79 | D->getSourceRange().getEnd()) |
80 | )) |
81 | return; |
82 | |
83 | PPCallbacks->seenQ_OBJECT = {};*/ |
84 | |
85 | ClassDef Def = Moc.parseClass(RD, ci.getSema()); |
86 | if (Def.HasQObject || Def.HasQGadget) { |
87 | objects.push_back(std::move(Def)); |
88 | ci.getPreprocessor().enableIncrementalProcessing(); |
89 | } |
90 | } |
91 | |
92 | bool MocASTConsumer::HandleTopLevelDecl(clang::DeclGroupRef D) |
93 | { |
94 | for (clang::Decl *Decl : D) { |
95 | if (clang::NamespaceDecl *NS = llvm::dyn_cast<clang::NamespaceDecl>(Decl)) { |
96 | if (!shouldParseDecl(Decl)) |
97 | continue; |
98 | HandleNamespaceDefinition(NS); |
99 | } |
100 | } |
101 | return clang::ASTConsumer::HandleTopLevelDecl(D); |
102 | } |
103 | |
104 | template<typename T> |
105 | static void operator+=(std::vector<T> &v1, const std::vector<T> &v2) |
106 | { |
107 | v1.insert(v1.end(), v2.begin(), v2.end()); |
108 | } |
109 | |
110 | void MocASTConsumer::HandleNamespaceDefinition(clang::NamespaceDecl* D) |
111 | { |
112 | // Try to find Q_NAMESPACE |
113 | NamespaceDef Def = Moc.parseNamespace(D, ci.getSema()); |
114 | if (Def.hasQNamespace) { |
115 | auto Canonical = D->getCanonicalDecl(); |
116 | auto it = std::find_if(namespaces.begin(), namespaces.end(), [&](const NamespaceDef &d) |
117 | { return d.Namespace && d.Namespace->getCanonicalDecl() == Canonical; }); |
118 | if (it == namespaces.end()) { |
119 | namespaces.push_back(std::move(Def)); |
120 | } else { |
121 | // merge the two. |
122 | it->Enums += Def.Enums; |
123 | it->Extra += Def.Extra; |
124 | it->ClassInfo += Def.ClassInfo; |
125 | } |
126 | ci.getPreprocessor().enableIncrementalProcessing(); |
127 | } |
128 | for (auto it = D->decls_begin(); it != D->decls_end(); ++it) { |
129 | if (clang::NamespaceDecl *NS = llvm::dyn_cast<clang::NamespaceDecl>(*it)) |
130 | HandleNamespaceDefinition(NS); |
131 | } |
132 | } |
133 | |
134 | |