1 //===--- PostfixOperatorCheck.cpp - clang-tidy-----------------------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "PostfixOperatorCheck.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/Lex/Lexer.h"
14 
15 using namespace clang::ast_matchers;
16 
17 namespace clang {
18 namespace tidy {
19 namespace cert {
20 
21 void PostfixOperatorCheck::registerMatchers(MatchFinder *Finder) {
22   if (!getLangOpts().CPlusPlus)
23     return;
24 
25   Finder->addMatcher(functionDecl(anyOf(hasOverloadedOperatorName("++"),
26                                         hasOverloadedOperatorName("--")))
27                          .bind("decl"),
28                      this);
29 }
30 
31 void PostfixOperatorCheck::check(const MatchFinder::MatchResult &Result) {
32   const auto *FuncDecl = Result.Nodes.getNodeAs<FunctionDecl>("decl");
33 
34   bool HasThis = false;
35   if (const auto *MethodDecl = dyn_cast<CXXMethodDecl>(FuncDecl))
36     HasThis = MethodDecl->isInstance();
37 
38   // Check if the operator is a postfix one.
39   if (FuncDecl->getNumParams() != (HasThis ? 1 : 2))
40     return;
41 
42   SourceRange ReturnRange = FuncDecl->getReturnTypeSourceRange();
43   SourceLocation Location = ReturnRange.getBegin();
44   if (!Location.isValid())
45     return;
46 
47   QualType ReturnType = FuncDecl->getReturnType();
48 
49   // Warn when the operators return a reference.
50   if (const auto *RefType = ReturnType->getAs<ReferenceType>()) {
51     auto Diag = diag(Location, "overloaded %0 returns a reference instead of a "
52                                "constant object type")
53                 << FuncDecl;
54 
55     if (Location.isMacroID() || ReturnType->getAs<TypedefType>() ||
56         RefType->getPointeeTypeAsWritten()->getAs<TypedefType>())
57       return;
58 
59     QualType ReplaceType =
60         ReturnType.getNonReferenceType().getLocalUnqualifiedType();
61     // The getReturnTypeSourceRange omits the qualifiers. We do not want to
62     // duplicate the const.
63     if (!ReturnType->getPointeeType().isConstQualified())
64       ReplaceType.addConst();
65 
66     Diag << FixItHint::CreateReplacement(
67         ReturnRange,
68         ReplaceType.getAsString(Result.Context->getPrintingPolicy()) + " ");
69 
70     return;
71   }
72 
73   if (ReturnType.isConstQualified() || ReturnType->isBuiltinType() ||
74       ReturnType->isPointerType())
75     return;
76 
77   auto Diag =
78       diag(Location, "overloaded %0 returns a non-constant object instead of a "
79                      "constant object type")
80       << FuncDecl;
81 
82   if (!Location.isMacroID() && !ReturnType->getAs<TypedefType>())
83     Diag << FixItHint::CreateInsertion(Location, "const ");
84 }
85 
86 } // namespace cert
87 } // namespace tidy
88 } // namespace clang
89