File indexing completed on 2024-05-12 04:39:11

0001 /*
0002     SPDX-FileCopyrightText: 2014 Kevin Funk <kfunk@kde.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005 */
0006 
0007 #include "clangdiagnosticevaluator.h"
0008 #include "unknowndeclarationproblem.h"
0009 #include "missingincludepathproblem.h"
0010 #include "util/clangtypes.h"
0011 
0012 namespace
0013 {
0014 
0015 /**
0016  * Check whether the problem stated in @p diagnostic may be caused by a missing include
0017  *
0018  * @return True if this may be fixable by adding a include, false otherwise
0019  */
0020 bool isDeclarationProblem(const QByteArray& description)
0021 {
0022     /* libclang does not currently expose an enum or any other way to query
0023      * what specific semantic error we're dealing with. Instead, we have to
0024      * parse the clang error message and guess if a missing include could be
0025      * the reason for the error
0026      *
0027      * There is no nice way of determining what identifier we're looking at either,
0028      * so we have to read that from the diagnostic too. Hopefully libclang will
0029      * get these features in the future.
0030      *
0031      * I have suggested this feature to clang devs. For reference, see:
0032      * http://lists.cs.uiuc.edu/pipermail/cfe-dev/2014-March/036036.html
0033      */
0034 
0035     return description.startsWith( QByteArrayLiteral("use of undeclared identifier") )
0036            || description.startsWith( QByteArrayLiteral("no member named") )
0037            || description.startsWith( QByteArrayLiteral("unknown type name") )
0038            || description.startsWith( QByteArrayLiteral("variable has incomplete type") )
0039            || description.startsWith( QByteArrayLiteral("member access into incomplete type") );
0040 }
0041 
0042 /// @return true if @p diagnostic says that include file not found
0043 bool isIncludeFileNotFound(const QByteArray& description)
0044 {
0045     return description.endsWith(QByteArrayLiteral("file not found"));
0046 }
0047 
0048 bool isReplaceWithDotProblem(const QByteArray& description)
0049 {
0050     // TODO: The diagnostic message depends on LibClang version.
0051     static const QByteArray diagnosticMessages[] = {
0052         QByteArrayLiteral("did you mean to use '.'?"),
0053         QByteArrayLiteral("maybe you meant to use '.'?")
0054     };
0055 
0056     for (const auto& diagnStr : diagnosticMessages) {
0057         if (description.endsWith(diagnStr)) {
0058             return true;
0059         }
0060     }
0061 
0062     return false;
0063 }
0064 
0065 bool isReplaceWithArrowProblem(const QByteArray& description)
0066 {
0067     // TODO: The diagnostic message depends on LibClang version.
0068     static const QByteArray diagnosticMessages[] = {
0069         QByteArrayLiteral("did you mean to use '->'?"),
0070         QByteArrayLiteral("maybe you meant to use '->'?")
0071     };
0072 
0073     for (const auto& diagnStr : diagnosticMessages) {
0074         if (description.endsWith(diagnStr)) {
0075             return true;
0076         }
0077     }
0078 
0079     return false;
0080 }
0081 
0082 }
0083 
0084 ClangDiagnosticEvaluator::DiagnosticType ClangDiagnosticEvaluator::diagnosticType(CXDiagnostic diagnostic)
0085 {
0086     const ClangString str(clang_getDiagnosticSpelling(diagnostic));
0087     const auto description = QByteArray::fromRawData(str.c_str(), qstrlen(str.c_str()));
0088 
0089     if (isDeclarationProblem(description)) {
0090         return UnknownDeclarationProblem;
0091     } else if (isIncludeFileNotFound(description)) {
0092         return IncludeFileNotFoundProblem;
0093     } else if (isReplaceWithDotProblem(description)) {
0094         return ReplaceWithDotProblem;
0095     } else if (isReplaceWithArrowProblem(description)) {
0096         return ReplaceWithArrowProblem;
0097     }
0098 
0099     return Unknown;
0100 }
0101 
0102 ClangProblem* ClangDiagnosticEvaluator::createProblem(CXDiagnostic diagnostic, CXTranslationUnit unit)
0103 {
0104     switch (diagnosticType(diagnostic)) {
0105     case IncludeFileNotFoundProblem:
0106         return new MissingIncludePathProblem(diagnostic, unit);
0107     case UnknownDeclarationProblem:
0108         return new class UnknownDeclarationProblem(diagnostic, unit);
0109     default:
0110         return new ClangProblem(diagnostic, unit);
0111     }
0112 }
0113