xref: /Universal-ctags/parsers/cxx/cxx_qtmoc.c (revision 6b1a862e526d5017f9f212a321f59d67c859d521)
1 /*
2  *  Copyright (c) 2017, Red Hat, Inc.
3 *   Copyright (c) 2017, Masatake YAMATO
4 *
5 *   This source code is released for free distribution under the terms of the
6 *   GNU General Public License version 2 or (at your option) any later version.
7 *
8 *   This module contains functions for handling Qt Moc tokens
9 */
10 
11 #include "general.h"
12 
13 #include "types.h"
14 
15 #include "debug.h"
16 #include "cxx_debug.h"
17 
18 #include "cxx_scope.h"
19 #include "cxx_parser_internal.h"
20 
21 #include "cxx_subparser.h"
22 
23 #include "keyword.h"
24 #include "read.h"
25 
26 #include <string.h>
27 
28 
29 typedef enum {
30 	K_SLOT,
31 	K_SIGNAL,
32 	K_PROPERTY,
33 } qtMocKind;
34 
35 static kindDefinition QtMocKinds [] = {
36 	{ true, 's', "slot", "slots" },
37 	{ true, 'S', "signal", "signals" },
38 	{ true, 'p', "property", "properties" },
39 };
40 
41 enum {
42 	KEYWORD_QOBJECT,
43 	KEYWORD_SIGNALS,
44 	KEYWORD_SLOTS,
45 	KEYWORD_PROPERTY,
46 };
47 
48 typedef int keywordId; /* to allow KEYWORD_NONE */
49 
50 static const keywordTable QtMocKeywordTable[] = {
51 	/* keyword			keyword ID */
52 	{ "Q_OBJECT",	KEYWORD_QOBJECT  },
53 	{ "Q_SIGNALS",	KEYWORD_SIGNALS	 },
54 	{ "signals",	KEYWORD_SIGNALS	 },
55 	{ "Q_SLOTS",	KEYWORD_SLOTS	 },
56 	{ "slots",		KEYWORD_SLOTS	 },
57 	{ "Q_PROPERTY", KEYWORD_PROPERTY },
58 };
59 
60 enum QtMocMemberMarker
61 {
62 	QtMocMemberMarkerNone = 0,
63 	QtMocMemberMarkerSlot,
64 	QtMocMemberMarkerSignal,
65 };
66 
67 struct sQtMocSubparser {
68 	struct sCxxSubparser cxx;
69 	int iBlockDepth;
70 	int iDepthOfQtClass;
71 	enum QtMocMemberMarker eMemberMarker;
72 };
73 
74 static langType Lang_QtMoc;
75 
cxxParserSkipToClosingParenthesisOrEOF(void)76 static bool cxxParserSkipToClosingParenthesisOrEOF(void)
77 {
78 	if(cxxTokenTypeIsOneOf(g_cxx.pToken,CXXTokenTypeClosingParenthesis | CXXTokenTypeEOF))
79 		return true;
80 
81 	return cxxParserParseUpToOneOf(CXXTokenTypeClosingParenthesis | CXXTokenTypeEOF,
82 								   false);
83 }
84 
qtMocMakeTagForProperty(CXXToken * pToken,const char * pszType)85 static void qtMocMakeTagForProperty (CXXToken * pToken, const char *pszType)
86 {
87 	tagEntryInfo tag;
88 
89 	initTagEntry(&tag,
90 				 vStringValue(pToken->pszWord),
91 				 K_PROPERTY);
92 	tag.lineNumber = pToken->iLineNumber;
93 	tag.filePosition = pToken->oFilePosition;
94 	tag.isFileScope = false;
95 
96 	if(!cxxScopeIsGlobal())
97 	{
98 		tag.extensionFields.scopeLangType = getNamedLanguage ("C++", 0); /* ??? */
99 		tag.extensionFields.scopeKindIndex = cxxScopeGetKind();
100 		tag.extensionFields.scopeName = cxxScopeGetFullName();
101 	}
102 
103 	tag.extensionFields.typeRef[0] = "typename";
104 	tag.extensionFields.typeRef[1] = pszType;
105 
106 	makeTagEntry(&tag);
107 }
108 
qtMocParseProperty(void)109 static bool qtMocParseProperty(void)
110 {
111 	char *pszPropType;
112 
113 	CXX_DEBUG_ENTER();
114 
115 	if(!cxxParserParseNextToken())
116 	{
117 		CXX_DEBUG_LEAVE_TEXT("EOF in cxxParserParseNextToken");
118 		return false;
119 	}
120 	if (!cxxTokenTypeIs(g_cxx.pToken, CXXTokenTypeOpeningParenthesis))
121 	{
122 		CXX_DEBUG_LEAVE_TEXT("Found no Opening Parenthesis after Q_PROPERTY");
123 		return false;
124 	}
125 
126 	if (!cxxParserParseNextToken())
127 	{
128 		CXX_DEBUG_LEAVE_TEXT("EOF in cxxParserParseNextToken");
129 		return false;
130 	}
131 	if (!(cxxTokenTypeIs(g_cxx.pToken, CXXTokenTypeIdentifier)
132 		  || (cxxTokenTypeIs(g_cxx.pToken, CXXTokenTypeKeyword)
133 			  && cxxKeywordMayBePartOfTypeName (g_cxx.pToken->eKeyword))))
134 
135 	{
136 		CXX_DEBUG_LEAVE_TEXT("Found no identifier after Q_PROPERTY(");
137 
138 		cxxParserSkipToClosingParenthesisOrEOF ();
139 		return false;
140 	}
141 
142 	pszPropType = vStringStrdup (g_cxx.pToken->pszWord);
143 	if(!cxxParserParseNextToken())
144 	{
145 		CXX_DEBUG_LEAVE_TEXT("EOF in cxxParserParseNextToken");
146 		eFree (pszPropType);
147 		return false;
148 	}
149 
150 	if (!cxxTokenTypeIs(g_cxx.pToken, CXXTokenTypeIdentifier))
151 	{
152 		CXX_DEBUG_LEAVE_TEXT("Found no identifier after Q_PROPERTY(%s", pszPropType);
153 		cxxParserSkipToClosingParenthesisOrEOF ();
154 		eFree (pszPropType);
155 		return false;
156 	}
157 
158 	qtMocMakeTagForProperty (g_cxx.pToken, pszPropType);
159 
160 	eFree (pszPropType);
161 	cxxParserSkipToClosingParenthesisOrEOF ();
162 
163 	CXX_DEBUG_LEAVE();
164 	return true;
165 }
166 
inputStart(subparser * s)167 static void inputStart(subparser *s)
168 {
169 	struct sQtMocSubparser *pQtMoc = (struct sQtMocSubparser*)s;
170 
171 	pQtMoc->iBlockDepth = 0;
172 	pQtMoc->iDepthOfQtClass = 0;
173 	pQtMoc->eMemberMarker = QtMocMemberMarkerNone;
174 }
175 
makeTagEntryNotify(subparser * s,const tagEntryInfo * entry,int corkIndex)176 static void makeTagEntryNotify (subparser *s, const tagEntryInfo *entry, int corkIndex)
177 {
178 	struct sQtMocSubparser *pQtMoc = (struct sQtMocSubparser*)s;
179 
180 	if (pQtMoc->iDepthOfQtClass == 0)
181 		return;
182 
183 	if ((pQtMoc->eMemberMarker != QtMocMemberMarkerNone) &&
184 		entry->kindIndex == CXXTagKindPROTOTYPE)
185 	{
186 		tagEntryInfo parasiteTag = *entry;
187 		parasiteTag.langType = getInputLanguage ();
188 		parasiteTag.kindIndex = (pQtMoc->eMemberMarker == QtMocMemberMarkerSlot)
189 			? K_SLOT
190 			: K_SIGNAL;
191 
192 		parasiteTag.extensionFields.scopeLangType = entry->langType;
193 		makeTagEntry (&parasiteTag);
194 	}
195 }
196 
enterBlockNotify(struct sCxxSubparser * pSubparser)197 static void enterBlockNotify (struct sCxxSubparser *pSubparser)
198 {
199 	struct sQtMocSubparser *pQtMoc = (struct sQtMocSubparser *)pSubparser;
200 
201 	pQtMoc->iBlockDepth++;
202 }
203 
leaveBlockNotify(struct sCxxSubparser * pSubparser)204 static void leaveBlockNotify (struct sCxxSubparser *pSubparser)
205 {
206 	struct sQtMocSubparser *pQtMoc = (struct sQtMocSubparser *)pSubparser;
207 
208 	if (pQtMoc->iDepthOfQtClass == pQtMoc->iBlockDepth)
209 		pQtMoc->iDepthOfQtClass = 0;
210 
211 	pQtMoc->iBlockDepth--;
212 }
213 
newIdentifierAsHeadOfMemberNotify(struct sCxxSubparser * pSubparser,CXXToken * pToken)214 static bool newIdentifierAsHeadOfMemberNotify (struct sCxxSubparser *pSubparser,
215 											   CXXToken *pToken)
216 {
217 	struct sQtMocSubparser *pQtMoc = (struct sQtMocSubparser *)pSubparser;
218 	keywordId keyword = lookupKeyword (vStringValue (pToken->pszWord), Lang_QtMoc);
219 
220 	if (keyword == KEYWORD_QOBJECT)
221 	{
222 		if (pQtMoc->iDepthOfQtClass == 0)
223 			pQtMoc->iDepthOfQtClass = pQtMoc->iBlockDepth;
224 		CXX_DEBUG_PRINT("Found \"Q_OBJECT\" Qt Object Marker in depth: %d",
225 						pQtMoc->iDepthOfQtClass);
226 		return true;
227 	}
228 	return false;
229 }
230 
unknownIdentifierInClassNotify(struct sCxxSubparser * pSubparser,CXXToken * pToken)231 static bool unknownIdentifierInClassNotify (struct sCxxSubparser *pSubparser,
232 											CXXToken *pToken)
233 {
234 	struct sQtMocSubparser *pQtMoc = (struct sQtMocSubparser *)pSubparser;
235 
236 	if (pQtMoc->iDepthOfQtClass == 0)
237 		return false;
238 
239 	keywordId keyword = lookupKeyword (vStringValue (pToken->pszWord), Lang_QtMoc);
240 
241 	switch (keyword)
242 	{
243 	case KEYWORD_SIGNALS:
244 		CXX_DEBUG_PRINT("Found \"signals\" QtMoc Keyword");
245 		pToken->eType = CXXTokenTypeKeyword;
246 		pToken->eKeyword = CXXKeywordPUBLIC;
247 		cxxParserParseAccessSpecifier();
248 		pQtMoc->eMemberMarker = QtMocMemberMarkerSignal;
249 		return true;
250 	case KEYWORD_SLOTS:
251 		CXX_DEBUG_PRINT("Found \"slots\" QtMoc Keyword");
252 		pToken->eType = CXXTokenTypeKeyword;
253 		g_cxx.pToken->eKeyword = CXXKeywordPUBLIC; /* ??? */
254 		cxxParserParseAccessSpecifier();
255 		pQtMoc->eMemberMarker = QtMocMemberMarkerSlot;
256 		return true;
257 	case KEYWORD_PROPERTY:
258 		CXX_DEBUG_PRINT("Found \"Q_PROPERTY\" QtMoc Keyword");
259 		qtMocParseProperty ();
260 		return true;
261 	default:
262 		break;
263 	}
264 
265 	return false;
266 }
267 
parseAccessSpecifierNotify(struct sCxxSubparser * pSubparser)268 static bool parseAccessSpecifierNotify(struct sCxxSubparser *pSubparser)
269 {
270 	struct sQtMocSubparser *pQtMoc = (struct sQtMocSubparser *)pSubparser;
271 
272 	if (pQtMoc->iBlockDepth > 0)
273 	{
274 		CXX_DEBUG_PRINT("Reset QtMoc member marker state");
275 		pQtMoc->eMemberMarker = QtMocMemberMarkerNone;
276 		return true;
277 	}
278 	return false;
279 }
280 
foundExtraIdentifierAsAccessSpecifier(struct sCxxSubparser * pSubparser,CXXToken * pToken)281 static void foundExtraIdentifierAsAccessSpecifier(struct sCxxSubparser *pSubparser,
282 												  CXXToken *pToken)
283 {
284 	struct sQtMocSubparser *pQtMoc = (struct sQtMocSubparser *)pSubparser;
285 	keywordId keyword = lookupKeyword (vStringValue (pToken->pszWord), Lang_QtMoc);
286 
287 	if (keyword == KEYWORD_SLOTS)
288 	{
289 		CXX_DEBUG_PRINT("Found \"slots\" QtMoc Keyword");
290 		pQtMoc->eMemberMarker = QtMocMemberMarkerSlot;
291 	}
292 }
293 
findQtMocTags(void)294 static void findQtMocTags(void)
295 {
296 	scheduleRunningBaseparser (0);
297 }
298 
initialize(langType lang)299 static void initialize (langType lang)
300 {
301 	Lang_QtMoc = lang;
302 }
303 
QtMocParser(void)304 extern parserDefinition* QtMocParser (void)
305 {
306 	parserDefinition* const def = parserNew("QtMoc");
307 
308 	static struct sQtMocSubparser qtMocSubparser = {
309 		.cxx = {
310 			.subparser = {
311 				.direction = SUBPARSER_BI_DIRECTION,
312 				.inputStart = inputStart,
313 				.makeTagEntryNotify = makeTagEntryNotify,
314 			},
315 			.enterBlockNotify = enterBlockNotify,
316 			.leaveBlockNotify = leaveBlockNotify,
317 			.newIdentifierAsHeadOfMemberNotify = newIdentifierAsHeadOfMemberNotify,
318 			.unknownIdentifierInClassNotify = unknownIdentifierInClassNotify,
319 			.parseAccessSpecifierNotify = parseAccessSpecifierNotify,
320 			.foundExtraIdentifierAsAccessSpecifier = foundExtraIdentifierAsAccessSpecifier,
321 		}
322 		/* The rest fields are initialized in inputStart(). */
323 	};
324 	static parserDependency dependencies [] = {
325 		[0] = { DEPTYPE_SUBPARSER, "C++", &qtMocSubparser },
326 	};
327 
328 	def->dependencies = dependencies;
329 	def->dependencyCount = ARRAY_SIZE (dependencies);
330 
331 	def->kindTable = QtMocKinds;
332 	def->kindCount = ARRAY_SIZE(QtMocKinds);
333 
334 	def->keywordTable = QtMocKeywordTable;
335 	def->keywordCount = ARRAY_SIZE (QtMocKeywordTable);
336 
337 	def->parser = findQtMocTags;
338 	def->initialize = initialize;
339 	def->useCork = CORK_QUEUE;
340 
341 	return def;
342 }
343