1*67e74705SXin Li //=- CheckObjCInstMethodRetTy.cpp - Check ObjC method signatures -*- C++ -*-==//
2*67e74705SXin Li //
3*67e74705SXin Li // The LLVM Compiler Infrastructure
4*67e74705SXin Li //
5*67e74705SXin Li // This file is distributed under the University of Illinois Open Source
6*67e74705SXin Li // License. See LICENSE.TXT for details.
7*67e74705SXin Li //
8*67e74705SXin Li //===----------------------------------------------------------------------===//
9*67e74705SXin Li //
10*67e74705SXin Li // This file defines a CheckObjCInstMethSignature, a flow-insenstive check
11*67e74705SXin Li // that determines if an Objective-C class interface incorrectly redefines
12*67e74705SXin Li // the method signature in a subclass.
13*67e74705SXin Li //
14*67e74705SXin Li //===----------------------------------------------------------------------===//
15*67e74705SXin Li
16*67e74705SXin Li #include "ClangSACheckers.h"
17*67e74705SXin Li #include "clang/AST/ASTContext.h"
18*67e74705SXin Li #include "clang/AST/DeclObjC.h"
19*67e74705SXin Li #include "clang/AST/Type.h"
20*67e74705SXin Li #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
21*67e74705SXin Li #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
22*67e74705SXin Li #include "clang/StaticAnalyzer/Core/Checker.h"
23*67e74705SXin Li #include "llvm/ADT/DenseMap.h"
24*67e74705SXin Li #include "llvm/Support/raw_ostream.h"
25*67e74705SXin Li
26*67e74705SXin Li using namespace clang;
27*67e74705SXin Li using namespace ento;
28*67e74705SXin Li
AreTypesCompatible(QualType Derived,QualType Ancestor,ASTContext & C)29*67e74705SXin Li static bool AreTypesCompatible(QualType Derived, QualType Ancestor,
30*67e74705SXin Li ASTContext &C) {
31*67e74705SXin Li
32*67e74705SXin Li // Right now don't compare the compatibility of pointers. That involves
33*67e74705SXin Li // looking at subtyping relationships. FIXME: Future patch.
34*67e74705SXin Li if (Derived->isAnyPointerType() && Ancestor->isAnyPointerType())
35*67e74705SXin Li return true;
36*67e74705SXin Li
37*67e74705SXin Li return C.typesAreCompatible(Derived, Ancestor);
38*67e74705SXin Li }
39*67e74705SXin Li
CompareReturnTypes(const ObjCMethodDecl * MethDerived,const ObjCMethodDecl * MethAncestor,BugReporter & BR,ASTContext & Ctx,const ObjCImplementationDecl * ID,const CheckerBase * Checker)40*67e74705SXin Li static void CompareReturnTypes(const ObjCMethodDecl *MethDerived,
41*67e74705SXin Li const ObjCMethodDecl *MethAncestor,
42*67e74705SXin Li BugReporter &BR, ASTContext &Ctx,
43*67e74705SXin Li const ObjCImplementationDecl *ID,
44*67e74705SXin Li const CheckerBase *Checker) {
45*67e74705SXin Li
46*67e74705SXin Li QualType ResDerived = MethDerived->getReturnType();
47*67e74705SXin Li QualType ResAncestor = MethAncestor->getReturnType();
48*67e74705SXin Li
49*67e74705SXin Li if (!AreTypesCompatible(ResDerived, ResAncestor, Ctx)) {
50*67e74705SXin Li std::string sbuf;
51*67e74705SXin Li llvm::raw_string_ostream os(sbuf);
52*67e74705SXin Li
53*67e74705SXin Li os << "The Objective-C class '"
54*67e74705SXin Li << *MethDerived->getClassInterface()
55*67e74705SXin Li << "', which is derived from class '"
56*67e74705SXin Li << *MethAncestor->getClassInterface()
57*67e74705SXin Li << "', defines the instance method '";
58*67e74705SXin Li MethDerived->getSelector().print(os);
59*67e74705SXin Li os << "' whose return type is '"
60*67e74705SXin Li << ResDerived.getAsString()
61*67e74705SXin Li << "'. A method with the same name (same selector) is also defined in "
62*67e74705SXin Li "class '"
63*67e74705SXin Li << *MethAncestor->getClassInterface()
64*67e74705SXin Li << "' and has a return type of '"
65*67e74705SXin Li << ResAncestor.getAsString()
66*67e74705SXin Li << "'. These two types are incompatible, and may result in undefined "
67*67e74705SXin Li "behavior for clients of these classes.";
68*67e74705SXin Li
69*67e74705SXin Li PathDiagnosticLocation MethDLoc =
70*67e74705SXin Li PathDiagnosticLocation::createBegin(MethDerived,
71*67e74705SXin Li BR.getSourceManager());
72*67e74705SXin Li
73*67e74705SXin Li BR.EmitBasicReport(
74*67e74705SXin Li MethDerived, Checker, "Incompatible instance method return type",
75*67e74705SXin Li categories::CoreFoundationObjectiveC, os.str(), MethDLoc);
76*67e74705SXin Li }
77*67e74705SXin Li }
78*67e74705SXin Li
CheckObjCInstMethSignature(const ObjCImplementationDecl * ID,BugReporter & BR,const CheckerBase * Checker)79*67e74705SXin Li static void CheckObjCInstMethSignature(const ObjCImplementationDecl *ID,
80*67e74705SXin Li BugReporter &BR,
81*67e74705SXin Li const CheckerBase *Checker) {
82*67e74705SXin Li
83*67e74705SXin Li const ObjCInterfaceDecl *D = ID->getClassInterface();
84*67e74705SXin Li const ObjCInterfaceDecl *C = D->getSuperClass();
85*67e74705SXin Li
86*67e74705SXin Li if (!C)
87*67e74705SXin Li return;
88*67e74705SXin Li
89*67e74705SXin Li ASTContext &Ctx = BR.getContext();
90*67e74705SXin Li
91*67e74705SXin Li // Build a DenseMap of the methods for quick querying.
92*67e74705SXin Li typedef llvm::DenseMap<Selector,ObjCMethodDecl*> MapTy;
93*67e74705SXin Li MapTy IMeths;
94*67e74705SXin Li unsigned NumMethods = 0;
95*67e74705SXin Li
96*67e74705SXin Li for (auto *M : ID->instance_methods()) {
97*67e74705SXin Li IMeths[M->getSelector()] = M;
98*67e74705SXin Li ++NumMethods;
99*67e74705SXin Li }
100*67e74705SXin Li
101*67e74705SXin Li // Now recurse the class hierarchy chain looking for methods with the
102*67e74705SXin Li // same signatures.
103*67e74705SXin Li while (C && NumMethods) {
104*67e74705SXin Li for (const auto *M : C->instance_methods()) {
105*67e74705SXin Li Selector S = M->getSelector();
106*67e74705SXin Li
107*67e74705SXin Li MapTy::iterator MI = IMeths.find(S);
108*67e74705SXin Li
109*67e74705SXin Li if (MI == IMeths.end() || MI->second == nullptr)
110*67e74705SXin Li continue;
111*67e74705SXin Li
112*67e74705SXin Li --NumMethods;
113*67e74705SXin Li ObjCMethodDecl *MethDerived = MI->second;
114*67e74705SXin Li MI->second = nullptr;
115*67e74705SXin Li
116*67e74705SXin Li CompareReturnTypes(MethDerived, M, BR, Ctx, ID, Checker);
117*67e74705SXin Li }
118*67e74705SXin Li
119*67e74705SXin Li C = C->getSuperClass();
120*67e74705SXin Li }
121*67e74705SXin Li }
122*67e74705SXin Li
123*67e74705SXin Li //===----------------------------------------------------------------------===//
124*67e74705SXin Li // ObjCMethSigsChecker
125*67e74705SXin Li //===----------------------------------------------------------------------===//
126*67e74705SXin Li
127*67e74705SXin Li namespace {
128*67e74705SXin Li class ObjCMethSigsChecker : public Checker<
129*67e74705SXin Li check::ASTDecl<ObjCImplementationDecl> > {
130*67e74705SXin Li public:
checkASTDecl(const ObjCImplementationDecl * D,AnalysisManager & mgr,BugReporter & BR) const131*67e74705SXin Li void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr,
132*67e74705SXin Li BugReporter &BR) const {
133*67e74705SXin Li CheckObjCInstMethSignature(D, BR, this);
134*67e74705SXin Li }
135*67e74705SXin Li };
136*67e74705SXin Li }
137*67e74705SXin Li
registerObjCMethSigsChecker(CheckerManager & mgr)138*67e74705SXin Li void ento::registerObjCMethSigsChecker(CheckerManager &mgr) {
139*67e74705SXin Li mgr.registerChecker<ObjCMethSigsChecker>();
140*67e74705SXin Li }
141