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