From: Steve Naroff Date: Tue, 16 Oct 2007 23:12:48 +0000 (+0000) Subject: Add Sema::CheckMessageArgumentTypes()... X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=81bfde990c327ba6e5225cf207d71431937730f3;p=clang Add Sema::CheckMessageArgumentTypes()... git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@43050 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/Sema/Sema.h b/Sema/Sema.h index 569891a454..5d36aced05 100644 --- a/Sema/Sema.h +++ b/Sema/Sema.h @@ -511,7 +511,7 @@ public: // from the Sel.getNumArgs(). TypeTy **ArgTypes, IdentifierInfo **ArgNames, AttributeList *AttrList, tok::ObjCKeywordKind MethodImplKind); - + // ActOnClassMessage - used for both unary and keyword messages. // ArgExprs is optional - if it is present, the number of expressions // is obtained from Sel.getNumArgs(). @@ -631,7 +631,11 @@ private: void CheckConstantInitList(QualType DeclType, InitListExpr *IList, QualType ElementType, bool isStatic, int &nInitializers, bool &hadError); - + + // returns true if there were any incompatible arguments. + bool CheckMessageArgumentTypes(Expr **Args, unsigned NumArgs, + ObjcMethodDecl *Method); + /// ConvertIntegerToTypeWarnOnOverflow - Convert the specified APInt to have /// the specified width and sign. If an overflow occurs, detect it and emit /// the specified diagnostic. diff --git a/Sema/SemaExpr.cpp b/Sema/SemaExpr.cpp index 37fd798568..e016cbb368 100644 --- a/Sema/SemaExpr.cpp +++ b/Sema/SemaExpr.cpp @@ -1928,6 +1928,67 @@ Sema::ExprResult Sema::ParseObjCSelectorExpression(Selector Sel, return new ObjCSelectorExpr(t, Sel, AtLoc, RParenLoc); } + +bool Sema::CheckMessageArgumentTypes(Expr **Args, unsigned NumArgs, + ObjcMethodDecl *Method) { + bool anyIncompatibleArgs = false; + + for (unsigned i = 0; i < NumArgs; i++) { + Expr *argExpr = Args[i]; + assert(argExpr && "CheckMessageArgumentTypes(): missing expression"); + + QualType lhsType = Method->getParamDecl(i)->getType(); + QualType rhsType = argExpr->getType(); + + // If necessary, apply function/array conversion. C99 6.7.5.3p[7,8]. + if (const ArrayType *ary = lhsType->getAsArrayType()) + lhsType = Context.getPointerType(ary->getElementType()); + else if (lhsType->isFunctionType()) + lhsType = Context.getPointerType(lhsType); + + AssignmentCheckResult result = CheckSingleAssignmentConstraints(lhsType, + argExpr); + if (Args[i] != argExpr) // The expression was converted. + Args[i] = argExpr; // Make sure we store the converted expression. + SourceLocation l = argExpr->getLocStart(); + + // decode the result (notice that AST's are still created for extensions). + switch (result) { + case Compatible: + break; + case PointerFromInt: + // check for null pointer constant (C99 6.3.2.3p3) + if (!argExpr->isNullPointerConstant(Context)) { + Diag(l, diag::ext_typecheck_sending_pointer_int, + lhsType.getAsString(), rhsType.getAsString(), + argExpr->getSourceRange()); + } + break; + case IntFromPointer: + Diag(l, diag::ext_typecheck_sending_pointer_int, + lhsType.getAsString(), rhsType.getAsString(), + argExpr->getSourceRange()); + break; + case IncompatiblePointer: + Diag(l, diag::ext_typecheck_sending_incompatible_pointer, + rhsType.getAsString(), lhsType.getAsString(), + argExpr->getSourceRange()); + break; + case CompatiblePointerDiscardsQualifiers: + Diag(l, diag::ext_typecheck_passing_discards_qualifiers, + rhsType.getAsString(), lhsType.getAsString(), + argExpr->getSourceRange()); + break; + case Incompatible: + Diag(l, diag::err_typecheck_sending_incompatible, + rhsType.getAsString(), lhsType.getAsString(), + argExpr->getSourceRange()); + anyIncompatibleArgs = true; + } + } + return anyIncompatibleArgs; +} + // ActOnClassMessage - used for both unary and keyword messages. // ArgExprs is optional - if it is present, the number of expressions // is obtained from Sel.getNumArgs(). @@ -1937,6 +1998,7 @@ Sema::ExprResult Sema::ActOnClassMessage( { assert(receiverName && "missing receiver class name"); + Expr **ArgExprs = reinterpret_cast(Args); ObjcInterfaceDecl* ClassDecl = getObjCInterfaceDecl(receiverName); ObjcMethodDecl *Method = ClassDecl->lookupClassMethod(Sel); QualType returnType; @@ -1946,9 +2008,11 @@ Sema::ExprResult Sema::ActOnClassMessage( returnType = GetObjcIdType(); } else { returnType = Method->getResultType(); + if (Sel.getNumArgs()) { + if (CheckMessageArgumentTypes(ArgExprs, Sel.getNumArgs(), Method)) + return true; + } } - // Expr *RExpr = global reference to the class symbol... - Expr **ArgExprs = reinterpret_cast(Args); return new ObjCMessageExpr(receiverName, Sel, returnType, lbrac, rbrac, ArgExprs); } @@ -1962,6 +2026,7 @@ Sema::ExprResult Sema::ActOnInstanceMessage( { assert(receiver && "missing receiver expression"); + Expr **ArgExprs = reinterpret_cast(Args); Expr *RExpr = static_cast(receiver); QualType receiverType = RExpr->getType(); QualType returnType; @@ -1974,6 +2039,9 @@ Sema::ExprResult Sema::ActOnInstanceMessage( returnType = GetObjcIdType(); } else { returnType = Method->getResultType(); + if (Sel.getNumArgs()) + if (CheckMessageArgumentTypes(ArgExprs, Sel.getNumArgs(), Method)) + return true; } } else { // FIXME (snaroff): checking in this code from Patrick. Needs to be @@ -1997,8 +2065,10 @@ Sema::ExprResult Sema::ActOnInstanceMessage( returnType = GetObjcIdType(); } else { returnType = Method->getResultType(); + if (Sel.getNumArgs()) + if (CheckMessageArgumentTypes(ArgExprs, Sel.getNumArgs(), Method)) + return true; } } - Expr **ArgExprs = reinterpret_cast(Args); return new ObjCMessageExpr(RExpr, Sel, returnType, lbrac, rbrac, ArgExprs); } diff --git a/include/clang/Basic/DiagnosticKinds.def b/include/clang/Basic/DiagnosticKinds.def index 2443be892f..eae6c22b88 100644 --- a/include/clang/Basic/DiagnosticKinds.def +++ b/include/clang/Basic/DiagnosticKinds.def @@ -763,6 +763,12 @@ DIAG(ext_typecheck_passing_pointer_int, WARNING, "incompatible types passing '%1' to function expecting '%0'") DIAG(ext_typecheck_passing_discards_qualifiers, WARNING, "passing '%0' to '%1' discards qualifiers") +DIAG(err_typecheck_sending_incompatible, ERROR, + "incompatible types passing '%0' to method expecting '%1'") +DIAG(ext_typecheck_sending_incompatible_pointer, WARNING, + "incompatible pointer types passing '%0' to method expecting '%1'") +DIAG(ext_typecheck_sending_pointer_int, WARNING, + "incompatible types passing '%1' to method expecting '%0'") DIAG(err_typecheck_cond_expect_scalar, ERROR, "used type '%0' where arithmetic or pointer type is required") DIAG(err_typecheck_expect_scalar_operand, ERROR, diff --git a/test/Sema/argument-checking.m b/test/Sema/argument-checking.m new file mode 100644 index 0000000000..cf7eb8386b --- /dev/null +++ b/test/Sema/argument-checking.m @@ -0,0 +1,27 @@ +// RUN: clang -fsyntax-only -verify %s + +typedef struct objc_object *id; + +struct S { int a; }; + +extern int charStarFunc(char *); +extern int charFunc(char); + +@interface Test ++alloc; +-(int)charStarMeth:(char *)s; +-structMeth:(struct S)s; +-structMeth:(struct S)s :(struct S)s2; +@end + +void test() { + id obj = [Test alloc]; + struct S sInst; + + charStarFunc(1); // expected-warning {{incompatible types passing 'int' to function expecting 'char *'}} + charFunc("abc"); // expected-warning {{incompatible types passing 'char *' to function expecting 'char'}} + + [obj charStarMeth:1]; // expected-warning {{incompatible types passing 'int' to method expecting 'char *'}} + [obj structMeth:1]; // expected-error {{incompatible types passing 'int' to method expecting 'struct S'}} + [obj structMeth:sInst :1]; // expected-error {{incompatible types passing 'int' to method expecting 'struct S'}} +}