120 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 "MacroUsageCheck.h"10#include "clang/Basic/TokenKinds.h"11#include "clang/Frontend/CompilerInstance.h"12#include "clang/Lex/PPCallbacks.h"13#include "clang/Lex/Preprocessor.h"14#include "llvm/ADT/STLExtras.h"15#include "llvm/Support/Regex.h"16#include <cctype>17#include <functional>18 19namespace clang::tidy::cppcoreguidelines {20 21static bool isCapsOnly(StringRef Name) {22 return llvm::all_of(Name, [](const char C) {23 return std::isupper(C) || std::isdigit(C) || C == '_';24 });25}26 27namespace {28 29class MacroUsageCallbacks : public PPCallbacks {30public:31 MacroUsageCallbacks(MacroUsageCheck *Check, const SourceManager &SM,32 StringRef RegExpStr, bool CapsOnly,33 bool IgnoreCommandLine)34 : Check(Check), SM(SM), RegExp(RegExpStr), CheckCapsOnly(CapsOnly),35 IgnoreCommandLineMacros(IgnoreCommandLine) {}36 void MacroDefined(const Token &MacroNameTok,37 const MacroDirective *MD) override {38 if (SM.isWrittenInBuiltinFile(MD->getLocation()) ||39 MD->getMacroInfo()->isUsedForHeaderGuard() ||40 MD->getMacroInfo()->tokens_empty() ||41 llvm::any_of(MD->getMacroInfo()->tokens(), [](const Token &T) {42 return T.isOneOf(tok::TokenKind::hash, tok::TokenKind::hashhash);43 }))44 return;45 46 if (IgnoreCommandLineMacros &&47 SM.isWrittenInCommandLineFile(MD->getLocation()))48 return;49 50 const StringRef MacroName = MacroNameTok.getIdentifierInfo()->getName();51 if (MacroName == "__GCC_HAVE_DWARF2_CFI_ASM")52 return;53 if (!CheckCapsOnly && !RegExp.match(MacroName))54 Check->warnMacro(MD, MacroName);55 56 if (CheckCapsOnly && !isCapsOnly(MacroName))57 Check->warnNaming(MD, MacroName);58 }59 60private:61 MacroUsageCheck *Check;62 const SourceManager &SM;63 const llvm::Regex RegExp;64 bool CheckCapsOnly;65 bool IgnoreCommandLineMacros;66};67} // namespace68 69void MacroUsageCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {70 Options.store(Opts, "AllowedRegexp", AllowedRegexp);71 Options.store(Opts, "CheckCapsOnly", CheckCapsOnly);72 Options.store(Opts, "IgnoreCommandLineMacros", IgnoreCommandLineMacros);73}74 75void MacroUsageCheck::registerPPCallbacks(const SourceManager &SM,76 Preprocessor *PP,77 Preprocessor *ModuleExpanderPP) {78 PP->addPPCallbacks(std::make_unique<MacroUsageCallbacks>(79 this, SM, AllowedRegexp, CheckCapsOnly, IgnoreCommandLineMacros));80}81 82void MacroUsageCheck::warnMacro(const MacroDirective *MD, StringRef MacroName) {83 const MacroInfo *Info = MD->getMacroInfo();84 StringRef Message;85 bool MacroBodyExpressionLike;86 if (Info->getNumTokens() > 0) {87 const Token &Tok = Info->getReplacementToken(0);88 // Now notice that keywords like `__attribute` cannot be a leading89 // token in an expression.90 MacroBodyExpressionLike = !Tok.is(tok::kw___attribute);91 } else {92 MacroBodyExpressionLike = true;93 }94 95 if (llvm::all_of(Info->tokens(), std::mem_fn(&Token::isLiteral)))96 Message = "macro '%0' used to declare a constant; consider using a "97 "'constexpr' constant";98 // A variadic macro is function-like at the same time. Therefore variadic99 // macros are checked first and will be excluded for the function-like100 // diagnostic.101 else if (Info->isVariadic() && MacroBodyExpressionLike)102 Message = "variadic macro '%0' used; consider using a 'constexpr' "103 "variadic template function";104 else if (Info->isFunctionLike() && MacroBodyExpressionLike)105 Message = "function-like macro '%0' used; consider a 'constexpr' template "106 "function";107 108 if (!Message.empty())109 diag(MD->getLocation(), Message) << MacroName;110}111 112void MacroUsageCheck::warnNaming(const MacroDirective *MD,113 StringRef MacroName) {114 diag(MD->getLocation(), "macro definition does not define the macro name "115 "'%0' using all uppercase characters")116 << MacroName;117}118 119} // namespace clang::tidy::cppcoreguidelines120