xref: /llvm-project/clang-tools-extra/clang-tidy/readability/AvoidReturnWithVoidValueCheck.cpp (revision b2c9f7d3188e41163574a83a835437955cf4b80f)
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 
AvoidReturnWithVoidValueCheck(StringRef Name,ClangTidyContext * Context)23 AvoidReturnWithVoidValueCheck::AvoidReturnWithVoidValueCheck(
24     StringRef Name, ClangTidyContext *Context)
25     : ClangTidyCheck(Name, Context),
26       IgnoreMacros(
27           Options.getLocalOrGlobal(IgnoreMacrosName, IgnoreMacrosDefault)),
28       StrictMode(Options.getLocalOrGlobal(StrictModeName, StrictModeDefault)) {}
29 
registerMatchers(MatchFinder * Finder)30 void AvoidReturnWithVoidValueCheck::registerMatchers(MatchFinder *Finder) {
31   Finder->addMatcher(
32       returnStmt(
33           hasReturnValue(allOf(hasType(voidType()), unless(initListExpr()))),
34           optionally(hasParent(
35               compoundStmt(
36                   optionally(hasParent(functionDecl().bind("function_parent"))))
37                   .bind("compound_parent"))))
38           .bind("void_return"),
39       this);
40 }
41 
check(const MatchFinder::MatchResult & Result)42 void AvoidReturnWithVoidValueCheck::check(
43     const MatchFinder::MatchResult &Result) {
44   const auto *VoidReturn = Result.Nodes.getNodeAs<ReturnStmt>("void_return");
45   if (IgnoreMacros && VoidReturn->getBeginLoc().isMacroID())
46     return;
47   const auto *SurroundingBlock =
48       Result.Nodes.getNodeAs<CompoundStmt>("compound_parent");
49   if (!StrictMode && !SurroundingBlock)
50     return;
51   DiagnosticBuilder Diag = diag(VoidReturn->getBeginLoc(),
52                                 "return statement within a void function "
53                                 "should not have a specified return value");
54   const SourceLocation SemicolonPos = utils::lexer::findNextTerminator(
55       VoidReturn->getEndLoc(), *Result.SourceManager, 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(VoidReturn->getReturnLoc());
67   const auto *FunctionParent =
68       Result.Nodes.getNodeAs<FunctionDecl>("function_parent");
69   if (!FunctionParent ||
70       (SurroundingBlock && SurroundingBlock->body_back() != VoidReturn))
71     // If this is not the last statement in a function body, we add a `return`.
72     Diag << FixItHint::CreateInsertion(SemicolonPos.getLocWithOffset(1),
73                                        " return;", true);
74 }
75 
storeOptions(ClangTidyOptions::OptionMap & Opts)76 void AvoidReturnWithVoidValueCheck::storeOptions(
77     ClangTidyOptions::OptionMap &Opts) {
78   Options.store(Opts, IgnoreMacrosName, IgnoreMacros);
79   Options.store(Opts, StrictModeName, StrictMode);
80 }
81 
82 } // namespace clang::tidy::readability
83