]> granicus.if.org Git - clang/commitdiff
Add Sema::CheckMessageArgumentTypes()...
authorSteve Naroff <snaroff@apple.com>
Tue, 16 Oct 2007 23:12:48 +0000 (23:12 +0000)
committerSteve Naroff <snaroff@apple.com>
Tue, 16 Oct 2007 23:12:48 +0000 (23:12 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@43050 91177308-0d34-0410-b5e6-96231b3b80d8

Sema/Sema.h
Sema/SemaExpr.cpp
include/clang/Basic/DiagnosticKinds.def
test/Sema/argument-checking.m [new file with mode: 0644]

index 569891a45412b97c28050f00a172eb8f7cbc368a..5d36aced05d67ffe1874e8ab7d9148891a0f313e 100644 (file)
@@ -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.
index 37fd798568f3148a9995442877a6537f8c0fe484..e016cbb36863d1e973011469318eeb38f41e3cdd 100644 (file)
@@ -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<Expr **>(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<Expr **>(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<Expr **>(Args);
   Expr *RExpr = static_cast<Expr *>(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<Expr **>(Args);
   return new ObjCMessageExpr(RExpr, Sel, returnType, lbrac, rbrac, ArgExprs);
 }
index 2443be892f95406a2b23f013e6825245a62d39e4..eae6c22b88ea23e857fab0dde52560f71b3b587a 100644 (file)
@@ -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 (file)
index 0000000..cf7eb83
--- /dev/null
@@ -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'}}
+}