xref: /llvm-project/clang-tools-extra/clang-tidy/readability/AvoidReturnWithVoidValueCheck.cpp (revision b2c9f7d3188e41163574a83a835437955cf4b80f)
1a89141f7SDanny Mösch //===--- AvoidReturnWithVoidValueCheck.cpp - clang-tidy -------------------===//
2a89141f7SDanny Mösch //
3a89141f7SDanny Mösch // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4a89141f7SDanny Mösch // See https://llvm.org/LICENSE.txt for license information.
5a89141f7SDanny Mösch // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6a89141f7SDanny Mösch //
7a89141f7SDanny Mösch //===----------------------------------------------------------------------===//
8a89141f7SDanny Mösch 
9a89141f7SDanny Mösch #include "AvoidReturnWithVoidValueCheck.h"
107a4e8976SDanny Mösch #include "../utils/BracesAroundStatement.h"
117a4e8976SDanny Mösch #include "../utils/LexerUtils.h"
12a89141f7SDanny Mösch 
13a89141f7SDanny Mösch using namespace clang::ast_matchers;
14a89141f7SDanny Mösch 
15a89141f7SDanny Mösch namespace clang::tidy::readability {
16a89141f7SDanny Mösch 
177a4e8976SDanny Mösch static constexpr char IgnoreMacrosName[] = "IgnoreMacros";
187a4e8976SDanny Mösch static const bool IgnoreMacrosDefault = true;
19a89141f7SDanny Mösch 
207a4e8976SDanny Mösch static constexpr char StrictModeName[] = "StrictMode";
217a4e8976SDanny Mösch static const bool StrictModeDefault = true;
22a89141f7SDanny Mösch 
AvoidReturnWithVoidValueCheck(StringRef Name,ClangTidyContext * Context)23a89141f7SDanny Mösch AvoidReturnWithVoidValueCheck::AvoidReturnWithVoidValueCheck(
24a89141f7SDanny Mösch     StringRef Name, ClangTidyContext *Context)
25a89141f7SDanny Mösch     : ClangTidyCheck(Name, Context),
26a89141f7SDanny Mösch       IgnoreMacros(
27a89141f7SDanny Mösch           Options.getLocalOrGlobal(IgnoreMacrosName, IgnoreMacrosDefault)),
28a89141f7SDanny Mösch       StrictMode(Options.getLocalOrGlobal(StrictModeName, StrictModeDefault)) {}
29a89141f7SDanny Mösch 
registerMatchers(MatchFinder * Finder)30a89141f7SDanny Mösch void AvoidReturnWithVoidValueCheck::registerMatchers(MatchFinder *Finder) {
31a89141f7SDanny Mösch   Finder->addMatcher(
32a89141f7SDanny Mösch       returnStmt(
33a89141f7SDanny Mösch           hasReturnValue(allOf(hasType(voidType()), unless(initListExpr()))),
347a4e8976SDanny Mösch           optionally(hasParent(
357a4e8976SDanny Mösch               compoundStmt(
367a4e8976SDanny Mösch                   optionally(hasParent(functionDecl().bind("function_parent"))))
377a4e8976SDanny Mösch                   .bind("compound_parent"))))
38a89141f7SDanny Mösch           .bind("void_return"),
39a89141f7SDanny Mösch       this);
40a89141f7SDanny Mösch }
41a89141f7SDanny Mösch 
check(const MatchFinder::MatchResult & Result)42a89141f7SDanny Mösch void AvoidReturnWithVoidValueCheck::check(
43a89141f7SDanny Mösch     const MatchFinder::MatchResult &Result) {
44a89141f7SDanny Mösch   const auto *VoidReturn = Result.Nodes.getNodeAs<ReturnStmt>("void_return");
45a89141f7SDanny Mösch   if (IgnoreMacros && VoidReturn->getBeginLoc().isMacroID())
46a89141f7SDanny Mösch     return;
477a4e8976SDanny Mösch   const auto *SurroundingBlock =
487a4e8976SDanny Mösch       Result.Nodes.getNodeAs<CompoundStmt>("compound_parent");
497a4e8976SDanny Mösch   if (!StrictMode && !SurroundingBlock)
50a89141f7SDanny Mösch     return;
517a4e8976SDanny Mösch   DiagnosticBuilder Diag = diag(VoidReturn->getBeginLoc(),
527a4e8976SDanny Mösch                                 "return statement within a void function "
53a89141f7SDanny Mösch                                 "should not have a specified return value");
547a4e8976SDanny Mösch   const SourceLocation SemicolonPos = utils::lexer::findNextTerminator(
557a4e8976SDanny Mösch       VoidReturn->getEndLoc(), *Result.SourceManager, getLangOpts());
567a4e8976SDanny Mösch   if (SemicolonPos.isInvalid())
577a4e8976SDanny Mösch     return;
587a4e8976SDanny Mösch   if (!SurroundingBlock) {
597a4e8976SDanny Mösch     const auto BraceInsertionHints = utils::getBraceInsertionsHints(
607a4e8976SDanny Mösch         VoidReturn, getLangOpts(), *Result.SourceManager,
617a4e8976SDanny Mösch         VoidReturn->getBeginLoc());
627a4e8976SDanny Mösch     if (BraceInsertionHints)
637a4e8976SDanny Mösch       Diag << BraceInsertionHints.openingBraceFixIt()
647a4e8976SDanny Mösch            << BraceInsertionHints.closingBraceFixIt();
657a4e8976SDanny Mösch   }
667a4e8976SDanny Mösch   Diag << FixItHint::CreateRemoval(VoidReturn->getReturnLoc());
67*b2c9f7d3SDanny Mösch   const auto *FunctionParent =
68*b2c9f7d3SDanny Mösch       Result.Nodes.getNodeAs<FunctionDecl>("function_parent");
69*b2c9f7d3SDanny Mösch   if (!FunctionParent ||
70*b2c9f7d3SDanny Mösch       (SurroundingBlock && SurroundingBlock->body_back() != VoidReturn))
71*b2c9f7d3SDanny Mösch     // If this is not the last statement in a function body, we add a `return`.
727a4e8976SDanny Mösch     Diag << FixItHint::CreateInsertion(SemicolonPos.getLocWithOffset(1),
737a4e8976SDanny Mösch                                        " return;", true);
74a89141f7SDanny Mösch }
75a89141f7SDanny Mösch 
storeOptions(ClangTidyOptions::OptionMap & Opts)76a89141f7SDanny Mösch void AvoidReturnWithVoidValueCheck::storeOptions(
77a89141f7SDanny Mösch     ClangTidyOptions::OptionMap &Opts) {
78a89141f7SDanny Mösch   Options.store(Opts, IgnoreMacrosName, IgnoreMacros);
79a89141f7SDanny Mösch   Options.store(Opts, StrictModeName, StrictMode);
80a89141f7SDanny Mösch }
81a89141f7SDanny Mösch 
82a89141f7SDanny Mösch } // namespace clang::tidy::readability
83