181 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 "IntegerTypesCheck.h"10#include "clang/AST/ASTContext.h"11#include "clang/ASTMatchers/ASTMatchFinder.h"12#include "clang/ASTMatchers/ASTMatchers.h"13#include "clang/Basic/AttrKinds.h"14#include "clang/Basic/CharInfo.h"15#include "clang/Basic/IdentifierTable.h"16#include "clang/Basic/TargetInfo.h"17#include "clang/Lex/Lexer.h"18 19namespace clang {20 21using namespace ast_matchers;22 23static Token getTokenAtLoc(SourceLocation Loc,24 const MatchFinder::MatchResult &MatchResult,25 IdentifierTable &IdentTable) {26 Token Tok;27 if (Lexer::getRawToken(Loc, Tok, *MatchResult.SourceManager,28 MatchResult.Context->getLangOpts(), false))29 return Tok;30 31 if (Tok.is(tok::raw_identifier)) {32 IdentifierInfo &Info = IdentTable.get(Tok.getRawIdentifier());33 Tok.setIdentifierInfo(&Info);34 Tok.setKind(Info.getTokenID());35 }36 return Tok;37}38 39namespace {40AST_MATCHER(FunctionDecl, isUserDefineLiteral) {41 return Node.getLiteralIdentifier() != nullptr;42}43 44AST_MATCHER(TypeLoc, isValidAndNotInMacro) {45 const SourceLocation Loc = Node.getBeginLoc();46 return Loc.isValid() && !Loc.isMacroID();47}48 49AST_MATCHER(TypeLoc, isBuiltinType) {50 TypeLoc TL = Node;51 if (auto QualLoc = Node.getAs<QualifiedTypeLoc>())52 TL = QualLoc.getUnqualifiedLoc();53 54 const auto BuiltinLoc = TL.getAs<BuiltinTypeLoc>();55 if (!BuiltinLoc)56 return false;57 58 switch (BuiltinLoc.getTypePtr()->getKind()) {59 case BuiltinType::Short:60 case BuiltinType::Long:61 case BuiltinType::LongLong:62 case BuiltinType::UShort:63 case BuiltinType::ULong:64 case BuiltinType::ULongLong:65 return true;66 default:67 return false;68 }69}70 71} // namespace72 73namespace tidy::google::runtime {74 75IntegerTypesCheck::IntegerTypesCheck(StringRef Name, ClangTidyContext *Context)76 : ClangTidyCheck(Name, Context),77 UnsignedTypePrefix(Options.get("UnsignedTypePrefix", "uint")),78 SignedTypePrefix(Options.get("SignedTypePrefix", "int")),79 TypeSuffix(Options.get("TypeSuffix", "")) {}80 81void IntegerTypesCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {82 Options.store(Opts, "UnsignedTypePrefix", UnsignedTypePrefix);83 Options.store(Opts, "SignedTypePrefix", SignedTypePrefix);84 Options.store(Opts, "TypeSuffix", TypeSuffix);85}86 87void IntegerTypesCheck::registerMatchers(MatchFinder *Finder) {88 // Match any integer types, unless they are passed to a printf-based API:89 //90 // https://google.github.io/styleguide/cppguide.html#64-bit_Portability91 // "Where possible, avoid passing arguments of types specified by92 // bitwidth typedefs to printf-based APIs."93 Finder->addMatcher(94 typeLoc(loc(isInteger()), isValidAndNotInMacro(), isBuiltinType(),95 unless(hasAncestor(96 callExpr(callee(functionDecl(hasAttr(attr::Format)))))),97 unless(hasParent(parmVarDecl(98 hasAncestor(functionDecl(isUserDefineLiteral()))))))99 .bind("tl"),100 this);101 IdentTable = std::make_unique<IdentifierTable>(getLangOpts());102}103 104void IntegerTypesCheck::check(const MatchFinder::MatchResult &Result) {105 auto TL = *Result.Nodes.getNodeAs<TypeLoc>("tl");106 const SourceLocation Loc = TL.getBeginLoc();107 108 // Look through qualification.109 if (auto QualLoc = TL.getAs<QualifiedTypeLoc>())110 TL = QualLoc.getUnqualifiedLoc();111 112 auto BuiltinLoc = TL.getAs<BuiltinTypeLoc>();113 if (!BuiltinLoc)114 return;115 116 const Token Tok = getTokenAtLoc(Loc, Result, *IdentTable);117 // Ensure the location actually points to one of the builting integral type118 // names we're interested in. Otherwise, we might be getting this match from119 // implicit code (e.g. an implicit assignment operator of a class containing120 // an array of non-POD types).121 if (!Tok.isOneOf(tok::kw_short, tok::kw_long, tok::kw_unsigned,122 tok::kw_signed))123 return;124 125 bool IsSigned = false;126 unsigned Width = 0;127 const TargetInfo &TargetInfo = Result.Context->getTargetInfo();128 129 // Look for uses of short, long, long long and their unsigned versions.130 switch (BuiltinLoc.getTypePtr()->getKind()) {131 case BuiltinType::Short:132 Width = TargetInfo.getShortWidth();133 IsSigned = true;134 break;135 case BuiltinType::Long:136 Width = TargetInfo.getLongWidth();137 IsSigned = true;138 break;139 case BuiltinType::LongLong:140 Width = TargetInfo.getLongLongWidth();141 IsSigned = true;142 break;143 case BuiltinType::UShort:144 Width = TargetInfo.getShortWidth();145 IsSigned = false;146 break;147 case BuiltinType::ULong:148 Width = TargetInfo.getLongWidth();149 IsSigned = false;150 break;151 case BuiltinType::ULongLong:152 Width = TargetInfo.getLongLongWidth();153 IsSigned = false;154 break;155 default:156 return;157 }158 159 // We allow "unsigned short port" as that's reasonably common and required by160 // the sockets API.161 const StringRef Port = "unsigned short port";162 const char *Data = Result.SourceManager->getCharacterData(Loc);163 if (!std::strncmp(Data, Port.data(), Port.size()) &&164 !isAsciiIdentifierContinue(Data[Port.size()]))165 return;166 167 const std::string Replacement =168 ((IsSigned ? SignedTypePrefix : UnsignedTypePrefix) + Twine(Width) +169 TypeSuffix)170 .str();171 172 // We don't add a fix-it as changing the type can easily break code,173 // e.g. when a function requires a 'long' argument on all platforms.174 // QualTypes are printed with implicit quotes.175 diag(Loc, "consider replacing %0 with '%1'")176 << BuiltinLoc.getType() << Replacement;177}178 179} // namespace tidy::google::runtime180} // namespace clang181