File indexing completed on 2024-04-14 05:32:07

0001 /*
0002     SPDX-FileCopyrightText: 2015 Klarälvdalens Datakonsult AB a KDAB Group company info@kdab.com
0003     SPDX-FileContributor: SĂ©rgio Martins <sergio.martins@kdab.com>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "FixItUtils.h"
0009 #include "StringUtils.h"
0010 
0011 #include <clang/AST/ASTContext.h>
0012 #include <clang/AST/Expr.h>
0013 #include <clang/AST/ExprCXX.h>
0014 #include <clang/AST/Stmt.h>
0015 #include <clang/Basic/Diagnostic.h>
0016 #include <clang/Basic/SourceLocation.h>
0017 #include <clang/Basic/SourceManager.h>
0018 #include <clang/Lex/Lexer.h>
0019 #include <clang/Lex/Token.h>
0020 
0021 using namespace clazy;
0022 using namespace clang;
0023 
0024 clang::FixItHint clazy::createReplacement(clang::SourceRange range, const std::string &replacement)
0025 {
0026     if (range.getBegin().isInvalid()) {
0027         return {};
0028     }
0029     return FixItHint::CreateReplacement(range, replacement);
0030 }
0031 
0032 clang::FixItHint clazy::createInsertion(clang::SourceLocation start, const std::string &insertion)
0033 {
0034     if (start.isInvalid()) {
0035         return {};
0036     }
0037     return FixItHint::CreateInsertion(start, insertion);
0038 }
0039 
0040 SourceRange clazy::rangeForLiteral(const ASTContext *context, StringLiteral *lt)
0041 {
0042     if (!lt) {
0043         return {};
0044     }
0045 
0046     const int numTokens = lt->getNumConcatenated();
0047     const SourceLocation lastTokenLoc = lt->getStrTokenLoc(numTokens - 1);
0048     if (lastTokenLoc.isInvalid()) {
0049         return {};
0050     }
0051 
0052     SourceRange range;
0053     range.setBegin(lt->getBeginLoc());
0054 
0055     SourceLocation end = Lexer::getLocForEndOfToken(lastTokenLoc,
0056                                                     0,
0057                                                     context->getSourceManager(),
0058                                                     context->getLangOpts()); // For some reason getLocStart(lt) is == to getLocEnd(lt)
0059 
0060     if (!end.isValid()) {
0061         return {};
0062     }
0063 
0064     range.setEnd(end);
0065     return range;
0066 }
0067 
0068 void clazy::insertParentMethodCall(const std::string &method, SourceRange range, std::vector<FixItHint> &fixits)
0069 {
0070     fixits.push_back(clazy::createInsertion(range.getEnd(), ")"));
0071     fixits.push_back(clazy::createInsertion(range.getBegin(), method + '('));
0072 }
0073 
0074 bool clazy::insertParentMethodCallAroundStringLiteral(const ASTContext *context, const std::string &method, StringLiteral *lt, std::vector<FixItHint> &fixits)
0075 {
0076     if (!lt) {
0077         return false;
0078     }
0079 
0080     const SourceRange range = rangeForLiteral(context, lt);
0081     if (range.isInvalid()) {
0082         return false;
0083     }
0084 
0085     insertParentMethodCall(method, range, /*by-ref*/ fixits);
0086     return true;
0087 }
0088 
0089 SourceLocation clazy::locForNextToken(const ASTContext *context, SourceLocation start, tok::TokenKind kind)
0090 {
0091     if (!start.isValid()) {
0092         return {};
0093     }
0094 
0095     Token result;
0096     Lexer::getRawToken(start, result, context->getSourceManager(), context->getLangOpts());
0097 
0098     if (result.getKind() == kind) {
0099         return start;
0100     }
0101 
0102     auto nextStart = Lexer::getLocForEndOfToken(start, 0, context->getSourceManager(), context->getLangOpts());
0103     if (nextStart.getRawEncoding() == start.getRawEncoding()) {
0104         return {};
0105     }
0106 
0107     return locForNextToken(context, nextStart, kind);
0108 }
0109 
0110 SourceLocation clazy::biggestSourceLocationInStmt(const SourceManager &sm, Stmt *stmt)
0111 {
0112     if (!stmt) {
0113         return {};
0114     }
0115 
0116     SourceLocation biggestLoc = stmt->getEndLoc();
0117 
0118     for (auto *child : stmt->children()) {
0119         SourceLocation candidateLoc = biggestSourceLocationInStmt(sm, child);
0120         if (candidateLoc.isValid() && sm.isBeforeInSLocAddrSpace(biggestLoc, candidateLoc)) {
0121             biggestLoc = candidateLoc;
0122         }
0123     }
0124 
0125     return biggestLoc;
0126 }
0127 
0128 SourceLocation clazy::locForEndOfToken(const ASTContext *context, SourceLocation start, int offset)
0129 {
0130     return Lexer::getLocForEndOfToken(start, offset, context->getSourceManager(), context->getLangOpts());
0131 }
0132 
0133 bool clazy::transformTwoCallsIntoOne(const ASTContext *context,
0134                                      CallExpr *call1,
0135                                      CXXMemberCallExpr *call2,
0136                                      const std::string &replacement,
0137                                      std::vector<FixItHint> &fixits)
0138 {
0139     Expr *implicitArgument = call2->getImplicitObjectArgument();
0140     if (!implicitArgument) {
0141         return false;
0142     }
0143 
0144     const SourceLocation start1 = call1->getBeginLoc();
0145     const SourceLocation end1 = clazy::locForEndOfToken(context, start1, -1); // -1 of offset, so we don't need to insert '('
0146     if (end1.isInvalid()) {
0147         return false;
0148     }
0149 
0150     const SourceLocation start2 = implicitArgument->getEndLoc();
0151     const SourceLocation end2 = call2->getEndLoc();
0152     if (start2.isInvalid() || end2.isInvalid()) {
0153         return false;
0154     }
0155 
0156     // qgetenv("foo").isEmpty()
0157     // ^                         start1
0158     //       ^                   end1
0159     //              ^            start2
0160     //                        ^  end2
0161     fixits.push_back(clazy::createReplacement({start1, end1}, replacement));
0162     fixits.push_back(clazy::createReplacement({start2, end2}, ")"));
0163 
0164     return true;
0165 }
0166 
0167 bool clazy::transformTwoCallsIntoOneV2(const ASTContext *context, CXXMemberCallExpr *call2, const std::string &replacement, std::vector<FixItHint> &fixits)
0168 {
0169     Expr *implicitArgument = call2->getImplicitObjectArgument();
0170     if (!implicitArgument) {
0171         return false;
0172     }
0173 
0174     SourceLocation start = implicitArgument->getBeginLoc();
0175     start = clazy::locForEndOfToken(context, start, 0);
0176     const SourceLocation end = call2->getEndLoc();
0177     if (start.isInvalid() || end.isInvalid()) {
0178         return false;
0179     }
0180 
0181     fixits.push_back(clazy::createReplacement({start, end}, replacement));
0182     return true;
0183 }
0184 
0185 FixItHint clazy::fixItReplaceWordWithWord(const ASTContext *context, clang::Stmt *begin, const std::string &replacement, const std::string &replacee)
0186 {
0187     const auto &sm = context->getSourceManager();
0188     SourceLocation rangeStart = begin->getBeginLoc();
0189     SourceLocation rangeEnd = Lexer::getLocForEndOfToken(rangeStart, -1, sm, context->getLangOpts());
0190 
0191     if (rangeEnd.isInvalid()) {
0192         // Fallback. Have seen a case in the wild where the above would fail, it's very rare
0193         rangeEnd = rangeStart.getLocWithOffset(replacee.size() - 2);
0194         if (rangeEnd.isInvalid()) {
0195             clazy::printLocation(sm, rangeStart);
0196             clazy::printLocation(sm, rangeEnd);
0197             clazy::printLocation(sm, Lexer::getLocForEndOfToken(rangeStart, 0, sm, context->getLangOpts()));
0198             return {};
0199         }
0200     }
0201 
0202     return FixItHint::CreateReplacement(SourceRange(rangeStart, rangeEnd), replacement);
0203 }
0204 
0205 std::vector<FixItHint> clazy::fixItRemoveToken(const ASTContext *context, Stmt *stmt, bool removeParenthesis)
0206 {
0207     SourceLocation start = stmt->getBeginLoc();
0208     SourceLocation end = Lexer::getLocForEndOfToken(start, removeParenthesis ? 0 : -1, context->getSourceManager(), context->getLangOpts());
0209 
0210     std::vector<FixItHint> fixits;
0211 
0212     if (start.isValid() && end.isValid()) {
0213         fixits.push_back(FixItHint::CreateRemoval(SourceRange(start, end)));
0214 
0215         if (removeParenthesis) {
0216             // Remove the last parenthesis
0217             fixits.push_back(FixItHint::CreateRemoval(SourceRange(stmt->getEndLoc(), stmt->getEndLoc())));
0218         }
0219     }
0220 
0221     return fixits;
0222 }