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 }