xref: /llvm-project/clang-tools-extra/clang-tidy/modernize/ReplaceRandomShuffleCheck.cpp (revision 76bbbcb41bcf4a1d7a26bb11b78cf97b60ea7d4b)
1 //===--- ReplaceRandomShuffleCheck.cpp - clang-tidy------------------------===//
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-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "ReplaceRandomShuffleCheck.h"
10 #include "../utils/FixItHintUtils.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/Frontend/CompilerInstance.h"
14 #include "clang/Lex/Preprocessor.h"
15 #include "clang/Tooling/FixIt.h"
16 
17 using namespace clang::ast_matchers;
18 
19 namespace clang::tidy::modernize {
20 
ReplaceRandomShuffleCheck(StringRef Name,ClangTidyContext * Context)21 ReplaceRandomShuffleCheck::ReplaceRandomShuffleCheck(StringRef Name,
22                                                      ClangTidyContext *Context)
23     : ClangTidyCheck(Name, Context),
24       IncludeInserter(Options.getLocalOrGlobal("IncludeStyle",
25                                                utils::IncludeSorter::IS_LLVM),
26                       areDiagsSelfContained()) {}
27 
registerMatchers(MatchFinder * Finder)28 void ReplaceRandomShuffleCheck::registerMatchers(MatchFinder *Finder) {
29   const auto Begin = hasArgument(0, expr());
30   const auto End = hasArgument(1, expr());
31   const auto RandomFunc = hasArgument(2, expr().bind("randomFunc"));
32   Finder->addMatcher(
33       traverse(
34           TK_AsIs,
35           callExpr(
36               anyOf(allOf(Begin, End, argumentCountIs(2)),
37                     allOf(Begin, End, RandomFunc, argumentCountIs(3))),
38               hasDeclaration(functionDecl(hasName("::std::random_shuffle"))),
39               has(implicitCastExpr(has(declRefExpr().bind("name")))))
40               .bind("match")),
41       this);
42 }
43 
registerPPCallbacks(const SourceManager & SM,Preprocessor * PP,Preprocessor * ModuleExpanderPP)44 void ReplaceRandomShuffleCheck::registerPPCallbacks(
45     const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
46   IncludeInserter.registerPreprocessor(PP);
47 }
48 
storeOptions(ClangTidyOptions::OptionMap & Opts)49 void ReplaceRandomShuffleCheck::storeOptions(
50     ClangTidyOptions::OptionMap &Opts) {
51   Options.store(Opts, "IncludeStyle", IncludeInserter.getStyle());
52 }
53 
check(const MatchFinder::MatchResult & Result)54 void ReplaceRandomShuffleCheck::check(const MatchFinder::MatchResult &Result) {
55   const auto *MatchedDecl = Result.Nodes.getNodeAs<DeclRefExpr>("name");
56   const auto *MatchedArgumentThree = Result.Nodes.getNodeAs<Expr>("randomFunc");
57   const auto *MatchedCallExpr = Result.Nodes.getNodeAs<CallExpr>("match");
58 
59   if (MatchedCallExpr->getBeginLoc().isMacroID())
60     return;
61 
62   auto Diag = [&] {
63     if (MatchedCallExpr->getNumArgs() == 3) {
64       auto DiagL =
65           diag(MatchedCallExpr->getBeginLoc(),
66                "'std::random_shuffle' has been removed in C++17; use "
67                "'std::shuffle' and an alternative random mechanism instead");
68       DiagL << FixItHint::CreateReplacement(
69           MatchedArgumentThree->getSourceRange(),
70           "std::mt19937(std::random_device()())");
71       return DiagL;
72     }
73     auto DiagL = diag(MatchedCallExpr->getBeginLoc(),
74                       "'std::random_shuffle' has been removed in C++17; use "
75                       "'std::shuffle' instead");
76     DiagL << FixItHint::CreateInsertion(
77         MatchedCallExpr->getRParenLoc(),
78         ", std::mt19937(std::random_device()())");
79     return DiagL;
80   }();
81 
82   std::string NewName = "shuffle";
83   StringRef ContainerText = Lexer::getSourceText(
84       CharSourceRange::getTokenRange(MatchedDecl->getSourceRange()),
85       *Result.SourceManager, getLangOpts());
86   if (ContainerText.starts_with("std::"))
87     NewName = "std::" + NewName;
88 
89   Diag << FixItHint::CreateRemoval(MatchedDecl->getSourceRange());
90   Diag << FixItHint::CreateInsertion(MatchedDecl->getBeginLoc(), NewName);
91   Diag << IncludeInserter.createIncludeInsertion(
92       Result.Context->getSourceManager().getFileID(
93           MatchedCallExpr->getBeginLoc()),
94       "<random>");
95 }
96 
97 } // namespace clang::tidy::modernize
98