1 | //===--- AvoidReturnWithVoidValueCheck.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 "AvoidReturnWithVoidValueCheck.h" |
10 | #include "../utils/BracesAroundStatement.h" |
11 | #include "../utils/LexerUtils.h" |
12 | |
13 | using namespace clang::ast_matchers; |
14 | |
15 | namespace clang::tidy::readability { |
16 | |
17 | static constexpr char IgnoreMacrosName[] = "IgnoreMacros" ; |
18 | static const bool IgnoreMacrosDefault = true; |
19 | |
20 | static constexpr char StrictModeName[] = "StrictMode" ; |
21 | static const bool StrictModeDefault = true; |
22 | |
23 | AvoidReturnWithVoidValueCheck::AvoidReturnWithVoidValueCheck( |
24 | StringRef Name, ClangTidyContext *Context) |
25 | : ClangTidyCheck(Name, Context), |
26 | IgnoreMacros( |
27 | Options.getLocalOrGlobal(LocalName: IgnoreMacrosName, Default: IgnoreMacrosDefault)), |
28 | StrictMode(Options.getLocalOrGlobal(LocalName: StrictModeName, Default: StrictModeDefault)) {} |
29 | |
30 | void AvoidReturnWithVoidValueCheck::registerMatchers(MatchFinder *Finder) { |
31 | Finder->addMatcher( |
32 | NodeMatch: returnStmt( |
33 | hasReturnValue(InnerMatcher: allOf(hasType(InnerMatcher: voidType()), unless(initListExpr()))), |
34 | optionally(hasParent( |
35 | compoundStmt( |
36 | optionally(hasParent(functionDecl().bind(ID: "function_parent" )))) |
37 | .bind(ID: "compound_parent" )))) |
38 | .bind(ID: "void_return" ), |
39 | Action: this); |
40 | } |
41 | |
42 | void AvoidReturnWithVoidValueCheck::check( |
43 | const MatchFinder::MatchResult &Result) { |
44 | const auto *VoidReturn = Result.Nodes.getNodeAs<ReturnStmt>(ID: "void_return" ); |
45 | if (IgnoreMacros && VoidReturn->getBeginLoc().isMacroID()) |
46 | return; |
47 | const auto *SurroundingBlock = |
48 | Result.Nodes.getNodeAs<CompoundStmt>(ID: "compound_parent" ); |
49 | if (!StrictMode && !SurroundingBlock) |
50 | return; |
51 | DiagnosticBuilder Diag = diag(Loc: VoidReturn->getBeginLoc(), |
52 | Description: "return statement within a void function " |
53 | "should not have a specified return value" ); |
54 | const SourceLocation SemicolonPos = utils::lexer::findNextTerminator( |
55 | Start: VoidReturn->getEndLoc(), SM: *Result.SourceManager, LangOpts: getLangOpts()); |
56 | if (SemicolonPos.isInvalid()) |
57 | return; |
58 | if (!SurroundingBlock) { |
59 | const auto BraceInsertionHints = utils::getBraceInsertionsHints( |
60 | VoidReturn, getLangOpts(), *Result.SourceManager, |
61 | VoidReturn->getBeginLoc()); |
62 | if (BraceInsertionHints) |
63 | Diag << BraceInsertionHints.openingBraceFixIt() |
64 | << BraceInsertionHints.closingBraceFixIt(); |
65 | } |
66 | Diag << FixItHint::CreateRemoval(RemoveRange: VoidReturn->getReturnLoc()); |
67 | if (!Result.Nodes.getNodeAs<FunctionDecl>(ID: "function_parent" ) || |
68 | SurroundingBlock->body_back() != VoidReturn) |
69 | Diag << FixItHint::CreateInsertion(InsertionLoc: SemicolonPos.getLocWithOffset(Offset: 1), |
70 | Code: " return;" , BeforePreviousInsertions: true); |
71 | } |
72 | |
73 | void AvoidReturnWithVoidValueCheck::storeOptions( |
74 | ClangTidyOptions::OptionMap &Opts) { |
75 | Options.store(Options&: Opts, LocalName: IgnoreMacrosName, Value: IgnoreMacros); |
76 | Options.store(Options&: Opts, LocalName: StrictModeName, Value: StrictMode); |
77 | } |
78 | |
79 | } // namespace clang::tidy::readability |
80 | |