File indexing completed on 2024-05-19 05:41:44

0001 /*
0002     SPDX-FileCopyrightText: 2023 Ahmad Samir <a.samirh78@gmail.com>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "sanitize-inline-keyword.h"
0008 
0009 #include "ClazyContext.h"
0010 #include "FixItUtils.h"
0011 #include "HierarchyUtils.h"
0012 #include "QtUtils.h"
0013 #include "TypeUtils.h"
0014 #include "Utils.h"
0015 
0016 #include <clang/AST/AST.h>
0017 #include <clang/Lex/Token.h>
0018 
0019 using namespace std::string_literals;
0020 using namespace clang;
0021 
0022 SanitizeInlineKeyword::SanitizeInlineKeyword(const std::string &name, ClazyContext *context)
0023     : CheckBase(name, context, Option_CanIgnoreIncludes)
0024 {
0025 }
0026 
0027 void SanitizeInlineKeyword::VisitDecl(Decl *decl)
0028 {
0029     auto *member = dyn_cast<CXXMethodDecl>(decl);
0030     if (!member) {
0031         return;
0032     }
0033 
0034     // The class this method belongs to
0035     auto *parentDecl = member->getParent();
0036     // Only exported classes
0037     if (!parentDecl || parentDecl->getVisibility() == clang::HiddenVisibility) {
0038         return;
0039     }
0040 
0041     // constexpr methods are implicitly inline
0042     if (member->isConstexpr()) {
0043         return;
0044     }
0045     // Function templates are implicitly inline
0046     if (member->isTemplateDecl()) {
0047         return;
0048     }
0049 
0050     // Is this CXXMethodDecl* inside the class body?
0051     if (member->isOutOfLine()) {
0052         return;
0053     }
0054 
0055     // Declared/defined in-class
0056     if (member->isThisDeclarationADefinition()) {
0057         return;
0058     }
0059 
0060     FunctionDecl *definition = member->getDefinition();
0061     if (!definition)
0062         return;
0063 
0064     auto name = clazy::name(definition);
0065 
0066     auto *cxxDefinition = dyn_cast<CXXMethodDecl>(definition);
0067     if (!cxxDefinition) {
0068         return;
0069     }
0070 
0071     if (name.empty()) {
0072         name = clazy::name(cxxDefinition); // E.g. operator[]
0073     }
0074 
0075     if (name.empty()) {
0076         return; // Can't emit a warning without a method name
0077     }
0078 
0079     auto defHasInline = [](auto def) {
0080         return def->isInlineSpecified() && def->isThisDeclarationADefinition() && def->isOutOfLine();
0081     };
0082 
0083     if (!member->isInlineSpecified() && defHasInline(cxxDefinition)) {
0084         std::string msg = std::string(name) + "(): "
0085             + "the 'inline' keyword is specified on the definition, but not the declaration. "
0086               "This could lead to hard-to-suppress warnings with some compilers (e.g. MinGW). "
0087               "The 'inline' keyword should be used for the declaration only.";
0088 
0089         SourceLocation loc = member->getBeginLoc();
0090         std::vector<FixItHint> fixits{clazy::createInsertion(loc, "inline "s)};
0091 
0092         SourceLocation def = cxxDefinition->getBeginLoc();
0093         SourceLocation defEnd = cxxDefinition->getEndLoc();
0094         Token tok;
0095         for (; def.isValid() && def != defEnd; def = Utils::locForNextToken(def, sm(), lo())) {
0096             if (!Lexer::getRawToken(def, tok, sm(), lo())) { // false means success!
0097                 if (tok.is(tok::raw_identifier) && tok.getRawIdentifier() == "inline"s) {
0098                     // Remove 'inline' from the definition
0099                     fixits.emplace_back(clazy::createReplacement(def, std::string()));
0100                     break;
0101                 }
0102             }
0103         }
0104 
0105         emitWarning(loc, msg, fixits);
0106     }
0107 }