clang-tools  14.0.0git
ConstReturnTypeCheck.cpp
Go to the documentation of this file.
1 //===--- ConstReturnTypeCheck.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 "ConstReturnTypeCheck.h"
10 #include "../utils/LexerUtils.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/Basic/SourceLocation.h"
14 #include "clang/Lex/Lexer.h"
15 #include "llvm/ADT/Optional.h"
16 
17 using namespace clang::ast_matchers;
18 
19 namespace clang {
20 namespace tidy {
21 namespace readability {
22 
23 // Finds the location of the qualifying `const` token in the `FunctionDecl`'s
24 // return type. Returns `None` when the return type is not `const`-qualified or
25 // `const` does not appear in `Def`'s source, like when the type is an alias or
26 // a macro.
27 static llvm::Optional<Token>
28 findConstToRemove(const FunctionDecl *Def,
29  const MatchFinder::MatchResult &Result) {
30  if (!Def->getReturnType().isLocalConstQualified())
31  return None;
32 
33  // Get the begin location for the function name, including any qualifiers
34  // written in the source (for out-of-line declarations). A FunctionDecl's
35  // "location" is the start of its name, so, when the name is unqualified, we
36  // use `getLocation()`.
37  SourceLocation NameBeginLoc = Def->getQualifier()
38  ? Def->getQualifierLoc().getBeginLoc()
39  : Def->getLocation();
40  // Since either of the locs can be in a macro, use `makeFileCharRange` to be
41  // sure that we have a consistent `CharSourceRange`, located entirely in the
42  // source file.
43  CharSourceRange FileRange = Lexer::makeFileCharRange(
44  CharSourceRange::getCharRange(Def->getBeginLoc(), NameBeginLoc),
45  *Result.SourceManager, Result.Context->getLangOpts());
46 
47  if (FileRange.isInvalid())
48  return None;
49 
51  tok::kw_const, FileRange, *Result.Context, *Result.SourceManager);
52 }
53 
54 namespace {
55 
56 struct CheckResult {
57  // Source range of the relevant `const` token in the definition being checked.
58  CharSourceRange ConstRange;
59 
60  // FixItHints associated with the definition being checked.
61  llvm::SmallVector<clang::FixItHint, 4> Hints;
62 
63  // Locations of any declarations that could not be fixed.
64  llvm::SmallVector<clang::SourceLocation, 4> DeclLocs;
65 };
66 
67 } // namespace
68 
69 // Does the actual work of the check.
70 static CheckResult checkDef(const clang::FunctionDecl *Def,
71  const MatchFinder::MatchResult &MatchResult) {
72  CheckResult Result;
73  llvm::Optional<Token> Tok = findConstToRemove(Def, MatchResult);
74  if (!Tok)
75  return Result;
76 
77  Result.ConstRange =
78  CharSourceRange::getCharRange(Tok->getLocation(), Tok->getEndLoc());
79  Result.Hints.push_back(FixItHint::CreateRemoval(Result.ConstRange));
80 
81  // Fix the definition and any visible declarations, but don't warn
82  // separately for each declaration. Instead, associate all fixes with the
83  // single warning at the definition.
84  for (const FunctionDecl *Decl = Def->getPreviousDecl(); Decl != nullptr;
85  Decl = Decl->getPreviousDecl()) {
86  if (llvm::Optional<Token> T = findConstToRemove(Decl, MatchResult))
87  Result.Hints.push_back(FixItHint::CreateRemoval(
88  CharSourceRange::getCharRange(T->getLocation(), T->getEndLoc())));
89  else
90  // `getInnerLocStart` gives the start of the return type.
91  Result.DeclLocs.push_back(Decl->getInnerLocStart());
92  }
93  return Result;
94 }
95 
96 void ConstReturnTypeCheck::registerMatchers(MatchFinder *Finder) {
97  // Find all function definitions for which the return types are `const`
98  // qualified.
99  Finder->addMatcher(
100  functionDecl(returns(isConstQualified()), isDefinition()).bind("func"),
101  this);
102 }
103 
104 void ConstReturnTypeCheck::check(const MatchFinder::MatchResult &Result) {
105  const auto *Def = Result.Nodes.getNodeAs<FunctionDecl>("func");
106  CheckResult CR = checkDef(Def, Result);
107  {
108  // Clang only supports one in-flight diagnostic at a time. So, delimit the
109  // scope of `Diagnostic` to allow further diagnostics after the scope. We
110  // use `getInnerLocStart` to get the start of the return type.
111  DiagnosticBuilder Diagnostic =
112  diag(Def->getInnerLocStart(),
113  "return type %0 is 'const'-qualified at the top level, which may "
114  "reduce code readability without improving const correctness")
115  << Def->getReturnType();
116  if (CR.ConstRange.isValid())
117  Diagnostic << CR.ConstRange;
118  for (auto &Hint : CR.Hints)
119  Diagnostic << Hint;
120  }
121  for (auto Loc : CR.DeclLocs)
122  diag(Loc, "could not transform this declaration", DiagnosticIDs::Note);
123 }
124 
125 } // namespace readability
126 } // namespace tidy
127 } // namespace clang
Loc
SourceLocation Loc
Definition: KernelNameRestrictionCheck.cpp:45
clang::tidy::readability::findConstToRemove
static llvm::Optional< Token > findConstToRemove(const FunctionDecl *Def, const MatchFinder::MatchResult &Result)
Definition: ConstReturnTypeCheck.cpp:28
clang::ast_matchers
Definition: AbseilMatcher.h:14
Diagnostic
DiagnosticCallback Diagnostic
Definition: ConfigCompile.cpp:101
Decl
const FunctionDecl * Decl
Definition: AvoidBindCheck.cpp:100
ConstReturnTypeCheck.h
ConstRange
CharSourceRange ConstRange
Definition: ConstReturnTypeCheck.cpp:58
Hints
llvm::SmallVector< clang::FixItHint, 4 > Hints
Definition: ConstReturnTypeCheck.cpp:61
DeclLocs
llvm::SmallVector< clang::SourceLocation, 4 > DeclLocs
Definition: ConstReturnTypeCheck.cpp:64
clang::clangd::check
bool check(llvm::StringRef File, llvm::function_ref< bool(const Position &)> ShouldCheckLine, const ThreadsafeFS &TFS, const ClangdLSPServer::Options &Opts, bool EnableCodeCompletion)
Definition: Check.cpp:259
clang::tidy::readability::checkDef
static CheckResult checkDef(const clang::FunctionDecl *Def, const MatchFinder::MatchResult &MatchResult)
Definition: ConstReturnTypeCheck.cpp:70
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::tidy::utils::lexer::getQualifyingToken
llvm::Optional< Token > getQualifyingToken(tok::TokenKind TK, CharSourceRange Range, const ASTContext &Context, const SourceManager &SM)
Assuming that Range spans a CVR-qualified type, returns the token in Range that is responsible for th...
Definition: LexerUtils.cpp:114