This ended up taking a little longer than expected because of places that exceed the spec.

Most of my diagnostics end up pointing to the 'main' symbol itself, rather than to types, because we don't seem to propagate source locations for types very reliably. I also wanted to implement more fixits, but again, no source range information (and that's hard for C types anyway).

John.
Index: test/SemaObjC/nsobject-attribute.m
===================================================================
--- test/SemaObjC/nsobject-attribute.m  (revision 77004)
+++ test/SemaObjC/nsobject-attribute.m  (working copy)
@@ -26,7 +26,7 @@ id getProperty(id self) {
 @synthesize x=x;
 @end
 
-int main(char *argc, char *argv[]) {
+int main(int argc, char *argv[]) {
     HandTested *to;
     to.x = tmp;  // setter
     if (tmp != to.x)
Index: test/Sema/block-args.c
===================================================================
--- test/Sema/block-args.c      (revision 77004)
+++ test/Sema/block-args.c      (working copy)
@@ -22,7 +22,7 @@ void test() {
   ^(int x, ...){return 5;}(arg, arg);   // Explicit varargs, ok.
 }
 
-int main(int argc) {
+int main(int argc, char** argv) {
   ^(int argCount) {
     argCount = 3;
   }(argc);
Index: test/CXX/basic/basic.start/basic.start.main/p2a.cpp
===================================================================
--- test/CXX/basic/basic.start/basic.start.main/p2a.cpp (revision 0)
+++ test/CXX/basic/basic.start/basic.start.main/p2a.cpp (revision 0)
@@ -0,0 +1,8 @@
+// RUN: clang-cc -fsyntax-only -verify %s 
+
+typedef int Int;
+typedef char Char;
+typedef Char* Carp;
+
+Int main(Int argc, Carp argv[]) {
+}
Index: test/CXX/basic/basic.start/basic.start.main/p2b.cpp
===================================================================
--- test/CXX/basic/basic.start/basic.start.main/p2b.cpp (revision 0)
+++ test/CXX/basic/basic.start/basic.start.main/p2b.cpp (revision 0)
@@ -0,0 +1,8 @@
+// RUN: clang-cc -fsyntax-only -verify %s 
+
+typedef int Int;
+typedef char Char;
+typedef Char* Carp;
+
+Int main(Int argc, Carp argv[], Char *env[]) {
+}
Index: test/CXX/basic/basic.start/basic.start.main/p2c.cpp
===================================================================
--- test/CXX/basic/basic.start/basic.start.main/p2c.cpp (revision 0)
+++ test/CXX/basic/basic.start/basic.start.main/p2c.cpp (revision 0)
@@ -0,0 +1,4 @@
+// RUN: clang-cc -fsyntax-only -verify %s 
+
+int main() {
+}
Index: test/CXX/basic/basic.start/basic.start.main/p2d.cpp
===================================================================
--- test/CXX/basic/basic.start/basic.start.main/p2d.cpp (revision 0)
+++ test/CXX/basic/basic.start/basic.start.main/p2d.cpp (revision 0)
@@ -0,0 +1,4 @@
+// RUN: clang-cc -fsyntax-only -verify %s 
+
+static int main() { // expected-error {{'main' is not allowed to be declared 
static}}
+}
Index: test/CXX/basic/basic.start/basic.start.main/p2e.cpp
===================================================================
--- test/CXX/basic/basic.start/basic.start.main/p2e.cpp (revision 0)
+++ test/CXX/basic/basic.start/basic.start.main/p2e.cpp (revision 0)
@@ -0,0 +1,4 @@
+// RUN: clang-cc -fsyntax-only -verify %s 
+
+inline int main() { // expected-error {{'main' is not allowed to be declared 
inline}}
+}
Index: test/CXX/basic/basic.start/basic.start.main/p2f.cpp
===================================================================
--- test/CXX/basic/basic.start/basic.start.main/p2f.cpp (revision 0)
+++ test/CXX/basic/basic.start/basic.start.main/p2f.cpp (revision 0)
@@ -0,0 +1,7 @@
+// RUN: clang-cc -fsyntax-only -verify %s 
+
+void  // expected-error {{error: 'main' must return 'int'}}
+main( // expected-error {{error: first argument of 'main' should be of type 
'int'}}
+     float a
+) {
+}
Index: test/CXX/basic/basic.start/basic.start.main/p2g.cpp
===================================================================
--- test/CXX/basic/basic.start/basic.start.main/p2g.cpp (revision 0)
+++ test/CXX/basic/basic.start/basic.start.main/p2g.cpp (revision 0)
@@ -0,0 +1,4 @@
+// RUN: clang-cc -fsyntax-only -verify %s 
+
+int main(int argc, const char* const* argv) {
+}
Index: test/CodeGen/volatile.c
===================================================================
--- test/CodeGen/volatile.c     (revision 77004)
+++ test/CodeGen/volatile.c     (working copy)
@@ -38,7 +38,7 @@ volatile extv4 vVE;
 
 volatile struct {int x;} aggFct(void);
 
-void main() {
+int main() {
   int i;
 
   // load
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td  (revision 77004)
+++ include/clang/Basic/DiagnosticSemaKinds.td  (working copy)
@@ -126,6 +126,19 @@ def err_builtin_definition : Error<"defi
 def err_types_compatible_p_in_cplusplus : Error<
   "__builtin_types_compatible_p is not valid in C++">;
 
+/// main()
+// static/inline main() are not errors in C, just in C++.
+def warn_unusual_main_decl : Warning<"'main' should not be declared "
+    "%select{static|inline|static or inline}0">;
+def err_unusual_main_decl : Error<"'main' is not allowed to be declared "
+    "%select{static|inline|static or inline}0">;
+def err_main_returns_nonint : Error<"'main' must return 'int'">;
+def err_main_surplus_args : Error<"%0 is too many arguments for 'main': "
+    "must be 0, 2, or 3">;
+def warn_main_one_arg : Warning<"one-argument 'main' is usually a mistake">;
+def err_main_arg_wrong : Error<"%select{first|second|third}0 argument of "
+    "'main' should be of type %1">;
+
 /// parser diagnostics
 def ext_typedef_without_a_name : ExtWarn<"typedef requires a name">;
 def err_statically_allocated_object : Error<
Index: lib/Sema/SemaDecl.cpp
===================================================================
--- lib/Sema/SemaDecl.cpp       (revision 77004)
+++ lib/Sema/SemaDecl.cpp       (working copy)
@@ -2767,7 +2767,80 @@ void Sema::CheckFunctionDeclaration(Func
 }
 
 void Sema::CheckMain(FunctionDecl* FD) {
-  // FIXME: implement.
+  // C++ [basic.start.main]p3:  A program that declares main to be inline
+  //   or static is ill-formed.
+  // This is not an error in C, but we can warn about it.
+  if (FD->isInline() || FD->getStorageClass() == FunctionDecl::Static) {
+    unsigned diagID = getLangOptions().CPlusPlus
+      ? diag::err_unusual_main_decl : diag::warn_unusual_main_decl;
+    int which = (FD->getStorageClass() == FunctionDecl::Static)
+              + (FD->isInline() << 1) - 1;
+    Diag(FD->getLocation(), diagID) << which;
+  }
+
+  QualType T = FD->getType();
+  assert(T->isFunctionType() && "function decl is not of function type");
+  const FunctionType* FT = T->getAsFunctionType();
+  
+  if (!Context.hasSameUnqualifiedType(FT->getResultType(), Context.IntTy)) {
+    // TODO: add a replacement fixit to turn the return type into 'int'.
+    Diag(FD->getTypeSpecStartLoc(), diag::err_main_returns_nonint);
+    FD->setInvalidDecl(true);
+  }
+
+  // Treat protoless main() as nullary.
+  if (isa<FunctionNoProtoType>(FT)) return;
+
+  const FunctionProtoType* FTP = cast<const FunctionProtoType>(FT);
+  unsigned nparams = FTP->getNumArgs();
+  assert(FD->getNumParams() == nparams);
+
+  if (nparams > 3) {
+    Diag(FD->getLocation(), diag::err_main_surplus_args) << nparams;
+    FD->setInvalidDecl(true);
+    nparams = 3;
+  }
+
+  // FIXME: a lot of the following diagnostics would be improved
+  // if we had some location information about types.
+
+  QualType CharPP =
+    Context.getPointerType(Context.getPointerType(Context.CharTy));
+  QualType Expected[] = { Context.IntTy, CharPP, CharPP };
+
+  for (unsigned i = 0; i < nparams; ++i) {
+    QualType AT = FTP->getArgType(i);
+
+    bool mismatch;
+    if (Context.hasSameUnqualifiedType(AT, Expected[i]))
+      mismatch = false;
+    else if (Expected[i] == CharPP) {
+      // As an extension, the following forms are okay:
+      //   char const **
+      //   char const * const *
+      //   char * const *
+      
+      if (const PointerType* PT = AT->getAsPointerType()) {
+        QualType ET = PT->getPointeeType();
+        ET.removeConst();
+        if (!ET.getCVRQualifiers() && (PT = ET->getAsPointerType())) {
+          ET = PT->getPointeeType();
+          ET.removeConst();
+          mismatch = (ET != Context.CharTy);
+        }else mismatch = true;
+      }else mismatch = true;
+    }else mismatch = true;
+
+    if (mismatch) {
+      Diag(FD->getLocation(), diag::err_main_arg_wrong) << i << Expected[i];
+      // TODO: suggest replacing given type with expected type
+      FD->setInvalidDecl(true);
+    }
+  }
+
+  if (nparams == 1 && !FD->isInvalidDecl()) {
+    Diag(FD->getLocation(), diag::warn_main_one_arg);
+  }
 }
 
 bool Sema::CheckForConstantInitializer(Expr *Init, QualType DclT) {
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to