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