1 /*
2 SPDX-FileCopyrightText: 2013 Milian Wolff <mail@milianw.de>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6
7 #include <KAboutData>
8
9 #include <language/util/debuglanguageparserhelper.h>
10
11 #include "../duchain/parsesession.h"
12 #include "../duchain/debugvisitor.h"
13 #include "../duchain/clangindex.h"
14 #include "../util/clangtypes.h"
15
16 using namespace KDevelop;
17 using namespace KDevelopUtils;
18
19 class ClangParser {
20 using TextStreamFunction = QTextStream& (*)(QTextStream&);
21 #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
22 static constexpr TextStreamFunction endl = Qt::endl;
23 #else
24 static constexpr TextStreamFunction endl = ::endl;
25 #endif
26
27 public:
ClangParser(const bool printAst,const bool printTokens)28 ClangParser(const bool printAst, const bool printTokens)
29 : m_session({})
30 , m_printAst(printAst)
31 , m_printTokens(printTokens)
32 {
33 }
34
35 /// parse contents of a file
parseFile(const QString & fileName)36 void parseFile( const QString &fileName )
37 {
38 if (!QFile::exists(fileName)) {
39 qerr << "File to parse does not exist: " << fileName << endl;
40 return;
41 }
42 m_session.setData(ParseSessionData::Ptr(new ParseSessionData({}, &m_index, environment(fileName))));
43 runSession();
44 }
45
46 /// parse code directly
parseCode(const QString & code)47 void parseCode( const QString &code )
48 {
49 const QString fileName = QStringLiteral("stdin.cpp");
50 m_session.setData(ParseSessionData::Ptr(new ParseSessionData({UnsavedFile(fileName, {code})},
51 &m_index, environment(fileName))));
52 runSession();
53 }
54
setIncludePaths(const QStringList & paths)55 void setIncludePaths(const QStringList& paths)
56 {
57 m_includePaths = paths;
58 }
59
setCustomArgs(const QString & args)60 void setCustomArgs(const QString& args)
61 {
62 m_customArgs = args;
63 }
64 private:
65 /**
66 * actually run the parse session
67 */
runSession()68 void runSession()
69 {
70 if (!m_session.unit()) {
71 qerr << "failed to parse code" << endl;
72 }
73 if (m_printTokens) {
74 CXTranslationUnit TU = m_session.unit();
75 auto cursor = clang_getTranslationUnitCursor(TU);
76 CXSourceRange range = clang_getCursorExtent(cursor);
77 const ClangTokens tokens(TU, range);
78 for (CXToken token : tokens) {
79 CXString spelling = clang_getTokenSpelling(TU, token);
80 qout << "token= " << clang_getCString(spelling) << endl;
81 clang_disposeString(spelling);
82 }
83 }
84
85 if (!m_session.unit()) {
86 qerr << "no AST tree could be generated" << endl;
87 exit(255);
88 return;
89 }
90
91 qout << "AST tree successfully generated" << endl;
92 auto file = m_session.mainFile();
93
94 if (m_printAst) {
95 DebugVisitor visitor(&m_session);
96 visitor.visit(m_session.unit(), file);
97 }
98
99 const auto problems = m_session.problemsForFile(file);
100 if (!problems.isEmpty()) {
101 qerr << endl << "problems encountered during parsing:" << endl;
102 for (const ProblemPointer& problem : problems) {
103 qerr << problem->toString() << endl;
104 }
105 } else {
106 qout << "no problems encountered during parsing" << endl;
107 }
108 }
109
environment(const QString & fileName) const110 ClangParsingEnvironment environment(const QString& fileName) const
111 {
112 ClangParsingEnvironment environment;
113 environment.setTranslationUnitUrl(IndexedString(fileName));
114 environment.addIncludes(toPathList(m_includePaths));
115 environment.addParserArguments(m_customArgs);
116 return environment;
117 }
118 ParseSession m_session;
119 const bool m_printAst;
120 const bool m_printTokens;
121 ClangIndex m_index;
122 QStringList m_includePaths;
123 QString m_customArgs;
124 };
125
126 namespace KDevelopUtils {
127 template<>
setupCustomArgs(QCommandLineParser * args)128 void setupCustomArgs<ClangParser>(QCommandLineParser* args)
129 {
130 args->addOption(QCommandLineOption{QStringList{"I", "include"}, i18n("add include path"), QStringLiteral("include")});
131 args->addOption(QCommandLineOption{QStringList{"custom-arg"}, i18n("custom clang args"), QStringLiteral("arg")});
132 }
133
134 template<>
setCustomArgs(ClangParser * parser,QCommandLineParser * args)135 void setCustomArgs<ClangParser>(ClangParser* parser, QCommandLineParser* args)
136 {
137 parser->setIncludePaths(args->values(QStringLiteral("include")));
138 parser->setCustomArgs(args->values(QStringLiteral("custom-arg")).join(QLatin1String(" ")));
139 }
140 }
141
main(int argc,char * argv[])142 int main(int argc, char* argv[])
143 {
144 KAboutData aboutData( QStringLiteral("clang-parser"), i18n( "clang-parser" ),
145 QStringLiteral("1"), i18n("KDevelop Clang parser debugging utility"), KAboutLicense::GPL,
146 i18n( "2013 Milian Wolff" ), QString(), QStringLiteral("https://www.kdevelop.org/") );
147
148 return KDevelopUtils::initAndRunParser<ClangParser>(aboutData, argc, argv);
149 }
150