File indexing completed on 2024-04-28 16:57:50
0001 /* 0002 This file is part of the clazy static checker. 0003 0004 Copyright (C) 2015 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com 0005 Author: Sérgio Martins <sergio.martins@kdab.com> 0006 0007 This library is free software; you can redistribute it and/or 0008 modify it under the terms of the GNU Library General Public 0009 License as published by the Free Software Foundation; either 0010 version 2 of the License, or (at your option) any later version. 0011 0012 This library is distributed in the hope that it will be useful, 0013 but WITHOUT ANY WARRANTY; without even the implied warranty of 0014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0015 Library General Public License for more details. 0016 0017 You should have received a copy of the GNU Library General Public License 0018 along with this library; see the file COPYING.LIB. If not, write to 0019 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0020 Boston, MA 02110-1301, USA. 0021 */ 0022 0023 #include "FixItUtils.h" 0024 #include "StringUtils.h" 0025 #include "SourceCompatibilityHelpers.h" 0026 0027 #include <clang/AST/Expr.h> 0028 #include <clang/AST/ExprCXX.h> 0029 #include <clang/Basic/Diagnostic.h> 0030 #include <clang/Lex/Lexer.h> 0031 #include <clang/AST/ASTContext.h> 0032 #include <clang/AST/Stmt.h> 0033 #include <clang/Basic/SourceLocation.h> 0034 #include <clang/Basic/SourceManager.h> 0035 #include <clang/Lex/Token.h> 0036 0037 using namespace clazy; 0038 using namespace clang; 0039 using namespace std; 0040 0041 clang::FixItHint clazy::createReplacement(clang::SourceRange range, const std::string &replacement) 0042 { 0043 if (range.getBegin().isInvalid()) { 0044 return {}; 0045 } else { 0046 return FixItHint::CreateReplacement(range, replacement); 0047 } 0048 } 0049 0050 clang::FixItHint clazy::createInsertion(clang::SourceLocation start, const std::string &insertion) 0051 { 0052 if (start.isInvalid()) { 0053 return {}; 0054 } else { 0055 return FixItHint::CreateInsertion(start, insertion); 0056 } 0057 } 0058 0059 SourceRange clazy::rangeForLiteral(const ASTContext *context, StringLiteral *lt) 0060 { 0061 if (!lt) 0062 return {}; 0063 0064 const int numTokens = lt->getNumConcatenated(); 0065 const SourceLocation lastTokenLoc = lt->getStrTokenLoc(numTokens - 1); 0066 if (lastTokenLoc.isInvalid()) { 0067 return {}; 0068 } 0069 0070 SourceRange range; 0071 range.setBegin(clazy::getLocStart(lt)); 0072 0073 SourceLocation end = Lexer::getLocForEndOfToken(lastTokenLoc, 0, 0074 context->getSourceManager(), 0075 context->getLangOpts()); // For some reason getLocStart(lt) is == to getLocEnd(lt) 0076 0077 if (!end.isValid()) { 0078 return {}; 0079 } 0080 0081 range.setEnd(end); 0082 return range; 0083 } 0084 0085 void clazy::insertParentMethodCall(const std::string &method, SourceRange range, std::vector<FixItHint> &fixits) 0086 { 0087 fixits.push_back(clazy::createInsertion(range.getEnd(), ")")); 0088 fixits.push_back(clazy::createInsertion(range.getBegin(), method + '(')); 0089 } 0090 0091 bool clazy::insertParentMethodCallAroundStringLiteral(const ASTContext *context, 0092 const std::string &method, 0093 StringLiteral *lt, 0094 std::vector<FixItHint> &fixits) 0095 { 0096 if (!lt) 0097 return false; 0098 0099 const SourceRange range = rangeForLiteral(context, lt); 0100 if (range.isInvalid()) 0101 return false; 0102 0103 insertParentMethodCall(method, range, /*by-ref*/ fixits); 0104 return true; 0105 } 0106 0107 SourceLocation clazy::locForNextToken(const ASTContext *context, SourceLocation start, tok::TokenKind kind) 0108 { 0109 if (!start.isValid()) 0110 return {}; 0111 0112 Token result; 0113 Lexer::getRawToken(start, result, context->getSourceManager(), context->getLangOpts()); 0114 0115 if (result.getKind() == kind) 0116 return start; 0117 0118 auto nextStart = Lexer::getLocForEndOfToken(start, 0, context->getSourceManager(), context->getLangOpts()); 0119 if (nextStart.getRawEncoding() == start.getRawEncoding()) 0120 return {}; 0121 0122 return locForNextToken(context, nextStart, kind); 0123 } 0124 0125 SourceLocation clazy::biggestSourceLocationInStmt(const SourceManager &sm, Stmt *stmt) 0126 { 0127 if (!stmt) 0128 return {}; 0129 0130 SourceLocation biggestLoc = getLocEnd(stmt); 0131 0132 for (auto child : stmt->children()) { 0133 SourceLocation candidateLoc = biggestSourceLocationInStmt(sm, child); 0134 if (candidateLoc.isValid() && sm.isBeforeInSLocAddrSpace(biggestLoc, candidateLoc)) 0135 biggestLoc = candidateLoc; 0136 } 0137 0138 return biggestLoc; 0139 } 0140 0141 SourceLocation clazy::locForEndOfToken(const ASTContext *context, SourceLocation start, int offset) 0142 { 0143 return Lexer::getLocForEndOfToken(start, offset, context->getSourceManager(), context->getLangOpts()); 0144 } 0145 0146 bool clazy::transformTwoCallsIntoOne(const ASTContext *context, CallExpr *call1, CXXMemberCallExpr *call2, 0147 const string &replacement, vector<FixItHint> &fixits) 0148 { 0149 Expr *implicitArgument = call2->getImplicitObjectArgument(); 0150 if (!implicitArgument) 0151 return false; 0152 0153 const SourceLocation start1 = clazy::getLocStart(call1); 0154 const SourceLocation end1 = clazy::locForEndOfToken(context, start1, -1); // -1 of offset, so we don't need to insert '(' 0155 if (end1.isInvalid()) 0156 return false; 0157 0158 const SourceLocation start2 = getLocEnd(implicitArgument); 0159 const SourceLocation end2 = getLocEnd(call2); 0160 if (start2.isInvalid() || end2.isInvalid()) 0161 return false; 0162 0163 // qgetenv("foo").isEmpty() 0164 // ^ start1 0165 // ^ end1 0166 // ^ start2 0167 // ^ end2 0168 fixits.push_back(clazy::createReplacement({ start1, end1 }, replacement)); 0169 fixits.push_back(clazy::createReplacement({ start2, end2 }, ")")); 0170 0171 return true; 0172 } 0173 0174 bool clazy::transformTwoCallsIntoOneV2(const ASTContext *context, CXXMemberCallExpr *call2, 0175 const string &replacement, std::vector<FixItHint> &fixits) 0176 { 0177 Expr *implicitArgument = call2->getImplicitObjectArgument(); 0178 if (!implicitArgument) 0179 return false; 0180 0181 SourceLocation start = clazy::getLocStart(implicitArgument); 0182 start = clazy::locForEndOfToken(context, start, 0); 0183 const SourceLocation end = getLocEnd(call2); 0184 if (start.isInvalid() || end.isInvalid()) 0185 return false; 0186 0187 fixits.push_back(clazy::createReplacement({ start, end }, replacement)); 0188 return true; 0189 } 0190 0191 FixItHint clazy::fixItReplaceWordWithWord(const ASTContext *context, clang::Stmt *begin, 0192 const string &replacement, const string &replacee) 0193 { 0194 auto &sm = context->getSourceManager(); 0195 SourceLocation rangeStart = clazy::getLocStart(begin); 0196 SourceLocation rangeEnd = Lexer::getLocForEndOfToken(rangeStart, -1, sm, context->getLangOpts()); 0197 0198 if (rangeEnd.isInvalid()) { 0199 // Fallback. Have seen a case in the wild where the above would fail, it's very rare 0200 rangeEnd = rangeStart.getLocWithOffset(replacee.size() - 2); 0201 if (rangeEnd.isInvalid()) { 0202 clazy::printLocation(sm, rangeStart); 0203 clazy::printLocation(sm, rangeEnd); 0204 clazy::printLocation(sm, Lexer::getLocForEndOfToken(rangeStart, 0, sm, context->getLangOpts())); 0205 return {}; 0206 } 0207 } 0208 0209 return FixItHint::CreateReplacement(SourceRange(rangeStart, rangeEnd), replacement); 0210 } 0211 0212 vector<FixItHint> clazy::fixItRemoveToken(const ASTContext *context, Stmt *stmt, bool removeParenthesis) 0213 { 0214 SourceLocation start = clazy::getLocStart(stmt); 0215 SourceLocation end = Lexer::getLocForEndOfToken(start, removeParenthesis ? 0 : -1, 0216 context->getSourceManager(), context->getLangOpts()); 0217 0218 vector<FixItHint> fixits; 0219 0220 if (start.isValid() && end.isValid()) { 0221 fixits.push_back(FixItHint::CreateRemoval(SourceRange(start, end))); 0222 0223 if (removeParenthesis) { 0224 // Remove the last parenthesis 0225 fixits.push_back(FixItHint::CreateRemoval(SourceRange(clazy::getLocEnd(stmt), clazy::getLocEnd(stmt)))); 0226 } 0227 } 0228 0229 return fixits; 0230 }