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