xref: /llvm-project/clang-tools-extra/clang-tidy/misc/MisplacedConstCheck.cpp (revision cbdc3e1bf9da09911ba353bcd20c6709bda43893)
1 //===--- MisplacedConstCheck.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 "MisplacedConstCheck.h"
10 #include "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
12 
13 using namespace clang::ast_matchers;
14 
15 namespace clang::tidy::misc {
16 
registerMatchers(MatchFinder * Finder)17 void MisplacedConstCheck::registerMatchers(MatchFinder *Finder) {
18   auto NonConstAndNonFunctionPointerType = hasType(pointerType(unless(
19       pointee(anyOf(isConstQualified(), ignoringParens(functionType()))))));
20 
21   Finder->addMatcher(
22       valueDecl(hasType(qualType(
23                     isConstQualified(),
24                     elaboratedType(namesType(typedefType(hasDeclaration(
25                         anyOf(typedefDecl(NonConstAndNonFunctionPointerType)
26                                   .bind("typedef"),
27                               typeAliasDecl(NonConstAndNonFunctionPointerType)
28                                   .bind("typeAlias")))))))))
29           .bind("decl"),
30       this);
31 }
32 
guessAlternateQualification(ASTContext & Context,QualType QT)33 static QualType guessAlternateQualification(ASTContext &Context, QualType QT) {
34   // We're given a QualType from a typedef where the qualifiers apply to the
35   // pointer instead of the pointee. Strip the const qualifier from the pointer
36   // type and add it to the pointee instead.
37   if (!QT->isPointerType())
38     return QT;
39 
40   Qualifiers Quals = QT.getLocalQualifiers();
41   Quals.removeConst();
42 
43   QualType NewQT = Context.getPointerType(
44       QualType(QT->getPointeeType().getTypePtr(), Qualifiers::Const));
45   return NewQT.withCVRQualifiers(Quals.getCVRQualifiers());
46 }
47 
check(const MatchFinder::MatchResult & Result)48 void MisplacedConstCheck::check(const MatchFinder::MatchResult &Result) {
49   const auto *Var = Result.Nodes.getNodeAs<ValueDecl>("decl");
50   ASTContext &Ctx = *Result.Context;
51   QualType CanQT = Var->getType().getCanonicalType();
52 
53   SourceLocation AliasLoc;
54   const char *AliasType = nullptr;
55   if (const auto *Typedef = Result.Nodes.getNodeAs<TypedefDecl>("typedef")) {
56     AliasLoc = Typedef->getLocation();
57     AliasType = "typedef";
58   } else if (const auto *TypeAlias =
59                  Result.Nodes.getNodeAs<TypeAliasDecl>("typeAlias")) {
60     AliasLoc = TypeAlias->getLocation();
61     AliasType = "type alias";
62   } else {
63     llvm_unreachable("registerMatchers has registered an unknown matcher,"
64                      " code out of sync");
65   }
66 
67   diag(Var->getLocation(), "%0 declared with a const-qualified %1; "
68                            "results in the type being '%2' instead of '%3'")
69       << Var << AliasType << CanQT.getAsString(Ctx.getPrintingPolicy())
70       << guessAlternateQualification(Ctx, CanQT)
71              .getAsString(Ctx.getPrintingPolicy());
72   diag(AliasLoc, "%0 declared here", DiagnosticIDs::Note) << AliasType;
73 }
74 
75 } // namespace clang::tidy::misc
76