1 | //===--- UseNoexceptCheck.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 "UseNoexceptCheck.h" |
10 | #include "clang/AST/ASTContext.h" |
11 | #include "clang/Lex/Lexer.h" |
12 | |
13 | using namespace clang::ast_matchers; |
14 | |
15 | namespace clang::tidy::modernize { |
16 | |
17 | namespace { |
18 | AST_MATCHER(NamedDecl, isValid) { return !Node.isInvalidDecl(); } |
19 | } // namespace |
20 | |
21 | UseNoexceptCheck::UseNoexceptCheck(StringRef Name, ClangTidyContext *Context) |
22 | : ClangTidyCheck(Name, Context), |
23 | NoexceptMacro(Options.get(LocalName: "ReplacementString" , Default: "" )), |
24 | UseNoexceptFalse(Options.get(LocalName: "UseNoexceptFalse" , Default: true)) {} |
25 | |
26 | void UseNoexceptCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { |
27 | Options.store(Options&: Opts, LocalName: "ReplacementString" , Value: NoexceptMacro); |
28 | Options.store(Options&: Opts, LocalName: "UseNoexceptFalse" , Value: UseNoexceptFalse); |
29 | } |
30 | |
31 | void UseNoexceptCheck::registerMatchers(MatchFinder *Finder) { |
32 | Finder->addMatcher( |
33 | NodeMatch: functionDecl( |
34 | isValid(), |
35 | hasTypeLoc(Inner: loc(InnerMatcher: functionProtoType(hasDynamicExceptionSpec()))), |
36 | optionally(cxxMethodDecl(anyOf(hasAnyOverloadedOperatorName( |
37 | "delete[]" , "delete" ), |
38 | cxxDestructorDecl())) |
39 | .bind(ID: "del-dtor" ))) |
40 | .bind(ID: "funcDecl" ), |
41 | Action: this); |
42 | |
43 | Finder->addMatcher( |
44 | NodeMatch: parmVarDecl(anyOf(hasType(InnerMatcher: pointerType(pointee(parenType(innerType( |
45 | functionProtoType(hasDynamicExceptionSpec())))))), |
46 | hasType(InnerMatcher: memberPointerType(pointee(parenType(innerType( |
47 | functionProtoType(hasDynamicExceptionSpec())))))))) |
48 | .bind(ID: "parmVarDecl" ), |
49 | Action: this); |
50 | } |
51 | |
52 | void UseNoexceptCheck::check(const MatchFinder::MatchResult &Result) { |
53 | const FunctionProtoType *FnTy = nullptr; |
54 | bool DtorOrOperatorDel = false; |
55 | SourceRange Range; |
56 | |
57 | if (const auto *FuncDecl = Result.Nodes.getNodeAs<FunctionDecl>(ID: "funcDecl" )) { |
58 | DtorOrOperatorDel = Result.Nodes.getNodeAs<FunctionDecl>(ID: "del-dtor" ); |
59 | FnTy = FuncDecl->getType()->getAs<FunctionProtoType>(); |
60 | if (const auto *TSI = FuncDecl->getTypeSourceInfo()) |
61 | Range = |
62 | TSI->getTypeLoc().castAs<FunctionTypeLoc>().getExceptionSpecRange(); |
63 | } else if (const auto *ParmDecl = |
64 | Result.Nodes.getNodeAs<ParmVarDecl>(ID: "parmVarDecl" )) { |
65 | FnTy = ParmDecl->getType() |
66 | ->castAs<Type>() |
67 | ->getPointeeType() |
68 | ->getAs<FunctionProtoType>(); |
69 | |
70 | if (const auto *TSI = ParmDecl->getTypeSourceInfo()) |
71 | Range = TSI->getTypeLoc() |
72 | .getNextTypeLoc() |
73 | .IgnoreParens() |
74 | .castAs<FunctionProtoTypeLoc>() |
75 | .getExceptionSpecRange(); |
76 | } |
77 | |
78 | assert(FnTy && "FunctionProtoType is null." ); |
79 | if (isUnresolvedExceptionSpec(ESpecType: FnTy->getExceptionSpecType())) |
80 | return; |
81 | |
82 | assert(Range.isValid() && "Exception Source Range is invalid." ); |
83 | |
84 | CharSourceRange CRange = Lexer::makeFileCharRange( |
85 | Range: CharSourceRange::getTokenRange(R: Range), SM: *Result.SourceManager, |
86 | LangOpts: Result.Context->getLangOpts()); |
87 | |
88 | bool IsNoThrow = FnTy->isNothrow(); |
89 | StringRef ReplacementStr = |
90 | IsNoThrow ? NoexceptMacro.empty() ? "noexcept" : NoexceptMacro |
91 | : NoexceptMacro.empty() |
92 | ? (DtorOrOperatorDel || UseNoexceptFalse) ? "noexcept(false)" : "" |
93 | : "" ; |
94 | |
95 | FixItHint FixIt; |
96 | if ((IsNoThrow || NoexceptMacro.empty()) && CRange.isValid()) |
97 | FixIt = FixItHint::CreateReplacement(RemoveRange: CRange, Code: ReplacementStr); |
98 | |
99 | diag(Loc: Range.getBegin(), Description: "dynamic exception specification '%0' is deprecated; " |
100 | "consider %select{using '%2'|removing it}1 instead" ) |
101 | << Lexer::getSourceText(Range: CRange, SM: *Result.SourceManager, |
102 | LangOpts: Result.Context->getLangOpts()) |
103 | << ReplacementStr.empty() << ReplacementStr << FixIt; |
104 | } |
105 | |
106 | } // namespace clang::tidy::modernize |
107 | |