138 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "RedundantInlineSpecifierCheck.h"10#include "clang/AST/ASTContext.h"11#include "clang/AST/Decl.h"12#include "clang/AST/DeclCXX.h"13#include "clang/AST/DeclTemplate.h"14#include "clang/AST/ExprCXX.h"15#include "clang/ASTMatchers/ASTMatchers.h"16#include "clang/Basic/Diagnostic.h"17#include "clang/Basic/SourceLocation.h"18#include "clang/Basic/SourceManager.h"19#include "clang/Lex/Token.h"20 21#include "../utils/LexerUtils.h"22 23using namespace clang::ast_matchers;24 25namespace clang::tidy::readability {26 27namespace {28AST_POLYMORPHIC_MATCHER(isInlineSpecified,29 AST_POLYMORPHIC_SUPPORTED_TYPES(FunctionDecl,30 VarDecl)) {31 if (const auto *FD = dyn_cast<FunctionDecl>(&Node))32 return FD->isInlineSpecified();33 if (const auto *VD = dyn_cast<VarDecl>(&Node))34 return VD->isInlineSpecified();35 llvm_unreachable("Not a valid polymorphic type");36}37 38AST_POLYMORPHIC_MATCHER_P(isInternalLinkage,39 AST_POLYMORPHIC_SUPPORTED_TYPES(FunctionDecl,40 VarDecl),41 bool, StrictMode) {42 if (!StrictMode)43 return false;44 if (const auto *FD = dyn_cast<FunctionDecl>(&Node))45 return FD->getStorageClass() == SC_Static || FD->isInAnonymousNamespace();46 if (const auto *VD = dyn_cast<VarDecl>(&Node))47 return VD->isInAnonymousNamespace();48 llvm_unreachable("Not a valid polymorphic type");49}50} // namespace51 52static SourceLocation getInlineTokenLocation(SourceRange RangeLocation,53 const SourceManager &Sources,54 const LangOptions &LangOpts) {55 const SourceLocation Loc = RangeLocation.getBegin();56 if (Loc.isMacroID())57 return {};58 59 Token FirstToken;60 Lexer::getRawToken(Loc, FirstToken, Sources, LangOpts, true);61 std::optional<Token> CurrentToken = FirstToken;62 while (CurrentToken && CurrentToken->getLocation() < RangeLocation.getEnd() &&63 CurrentToken->isNot(tok::eof)) {64 if (CurrentToken->is(tok::raw_identifier) &&65 CurrentToken->getRawIdentifier() == "inline")66 return CurrentToken->getLocation();67 68 CurrentToken =69 Lexer::findNextToken(CurrentToken->getLocation(), Sources, LangOpts);70 }71 return {};72}73 74void RedundantInlineSpecifierCheck::registerMatchers(MatchFinder *Finder) {75 const auto IsPartOfRecordDecl = hasAncestor(recordDecl());76 Finder->addMatcher(77 functionDecl(isInlineSpecified(),78 anyOf(isConstexpr(), isDeleted(),79 allOf(isDefaulted(), IsPartOfRecordDecl),80 isInternalLinkage(StrictMode),81 allOf(isDefinition(), IsPartOfRecordDecl)))82 .bind("fun_decl"),83 this);84 85 if (StrictMode)86 Finder->addMatcher(87 functionTemplateDecl(88 has(functionDecl(allOf(isInlineSpecified(), isDefinition()))))89 .bind("templ_decl"),90 this);91 92 if (getLangOpts().CPlusPlus17) {93 Finder->addMatcher(94 varDecl(95 isInlineSpecified(),96 anyOf(allOf(isInternalLinkage(StrictMode),97 unless(allOf(hasInitializer(expr()), IsPartOfRecordDecl,98 isStaticStorageClass()))),99 allOf(isConstexpr(), IsPartOfRecordDecl)))100 .bind("var_decl"),101 this);102 }103}104 105template <typename T>106void RedundantInlineSpecifierCheck::handleMatchedDecl(107 const T *MatchedDecl, const SourceManager &Sources,108 const MatchFinder::MatchResult &Result, StringRef Message) {109 const SourceLocation Loc = getInlineTokenLocation(110 MatchedDecl->getSourceRange(), Sources, Result.Context->getLangOpts());111 if (Loc.isValid())112 diag(Loc, Message) << MatchedDecl << FixItHint::CreateRemoval(Loc);113}114 115void RedundantInlineSpecifierCheck::check(116 const MatchFinder::MatchResult &Result) {117 const SourceManager &Sources = *Result.SourceManager;118 119 if (const auto *MatchedDecl =120 Result.Nodes.getNodeAs<FunctionDecl>("fun_decl")) {121 handleMatchedDecl(122 MatchedDecl, Sources, Result,123 "function %0 has inline specifier but is implicitly inlined");124 } else if (const auto *MatchedDecl =125 Result.Nodes.getNodeAs<VarDecl>("var_decl")) {126 handleMatchedDecl(127 MatchedDecl, Sources, Result,128 "variable %0 has inline specifier but is implicitly inlined");129 } else if (const auto *MatchedDecl =130 Result.Nodes.getNodeAs<FunctionTemplateDecl>("templ_decl")) {131 handleMatchedDecl(132 MatchedDecl, Sources, Result,133 "function %0 has inline specifier but is implicitly inlined");134 }135}136 137} // namespace clang::tidy::readability138