From 02dee0a46a390ad3bbad16672bd44c62628ea1ce Mon Sep 17 00:00:00 2001 From: John McCall Date: Sat, 25 Jul 2009 04:36:53 +0000 Subject: [PATCH] Semantic checking for main(). Fix some invalid main() methods in the test suite that were nicely exposed by the new checks. llvm-svn: 77047 --- clang/include/clang/Basic/DiagnosticSemaKinds.td | 13 ++++ clang/lib/Sema/SemaDecl.cpp | 80 +++++++++++++++++++++- .../CXX/basic/basic.start/basic.start.main/p2a.cpp | 8 +++ .../CXX/basic/basic.start/basic.start.main/p2b.cpp | 8 +++ .../CXX/basic/basic.start/basic.start.main/p2c.cpp | 4 ++ .../CXX/basic/basic.start/basic.start.main/p2d.cpp | 4 ++ .../CXX/basic/basic.start/basic.start.main/p2e.cpp | 4 ++ .../CXX/basic/basic.start/basic.start.main/p2f.cpp | 7 ++ .../CXX/basic/basic.start/basic.start.main/p2g.cpp | 4 ++ clang/test/CodeGen/volatile.c | 2 +- clang/test/Sema/block-args.c | 2 +- clang/test/SemaObjC/nsobject-attribute.m | 2 +- 12 files changed, 134 insertions(+), 4 deletions(-) create mode 100644 clang/test/CXX/basic/basic.start/basic.start.main/p2a.cpp create mode 100644 clang/test/CXX/basic/basic.start/basic.start.main/p2b.cpp create mode 100644 clang/test/CXX/basic/basic.start/basic.start.main/p2c.cpp create mode 100644 clang/test/CXX/basic/basic.start/basic.start.main/p2d.cpp create mode 100644 clang/test/CXX/basic/basic.start/basic.start.main/p2e.cpp create mode 100644 clang/test/CXX/basic/basic.start/basic.start.main/p2f.cpp create mode 100644 clang/test/CXX/basic/basic.start/basic.start.main/p2g.cpp diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 9540091..9d2b2a9 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -126,6 +126,19 @@ def err_builtin_definition : Error<"definition of builtin function %0">; 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< diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 4548965..91de998 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -2767,7 +2767,85 @@ void Sema::CheckFunctionDeclaration(FunctionDecl *NewFD, NamedDecl *&PrevDecl, } 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. + // C99 6.7.4p4: In a hosted environment, the inline function specifier + // shall not appear in a declaration of main. + // static main is not an error under C99, but we should warn about it. + bool isInline = FD->isInline(); + bool isStatic = FD->getStorageClass() == FunctionDecl::Static; + if (isInline || isStatic) { + unsigned diagID = diag::warn_unusual_main_decl; + if (isInline || getLangOptions().CPlusPlus) + diagID = diag::err_unusual_main_decl; + + int which = isStatic + (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(FT)) return; + + const FunctionProtoType* FTP = cast(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 = true; + + 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 * + + QualifierSet qs; + const PointerType* PT; + if ((PT = qs.strip(AT)->getAsPointerType()) && + (PT = qs.strip(PT->getPointeeType())->getAsPointerType()) && + (QualType(qs.strip(PT->getPointeeType()), 0) == Context.CharTy)) { + qs.removeConst(); + mismatch = !qs.empty(); + } + } + + 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) { diff --git a/clang/test/CXX/basic/basic.start/basic.start.main/p2a.cpp b/clang/test/CXX/basic/basic.start/basic.start.main/p2a.cpp new file mode 100644 index 0000000..a6a7587 --- /dev/null +++ b/clang/test/CXX/basic/basic.start/basic.start.main/p2a.cpp @@ -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[]) { +} diff --git a/clang/test/CXX/basic/basic.start/basic.start.main/p2b.cpp b/clang/test/CXX/basic/basic.start/basic.start.main/p2b.cpp new file mode 100644 index 0000000..caecf60 --- /dev/null +++ b/clang/test/CXX/basic/basic.start/basic.start.main/p2b.cpp @@ -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[]) { +} diff --git a/clang/test/CXX/basic/basic.start/basic.start.main/p2c.cpp b/clang/test/CXX/basic/basic.start/basic.start.main/p2c.cpp new file mode 100644 index 0000000..8587d8c --- /dev/null +++ b/clang/test/CXX/basic/basic.start/basic.start.main/p2c.cpp @@ -0,0 +1,4 @@ +// RUN: clang-cc -fsyntax-only -verify %s + +int main() { +} diff --git a/clang/test/CXX/basic/basic.start/basic.start.main/p2d.cpp b/clang/test/CXX/basic/basic.start/basic.start.main/p2d.cpp new file mode 100644 index 0000000..777b5ce --- /dev/null +++ b/clang/test/CXX/basic/basic.start/basic.start.main/p2d.cpp @@ -0,0 +1,4 @@ +// RUN: clang-cc -fsyntax-only -verify %s + +static int main() { // expected-error {{'main' is not allowed to be declared static}} +} diff --git a/clang/test/CXX/basic/basic.start/basic.start.main/p2e.cpp b/clang/test/CXX/basic/basic.start/basic.start.main/p2e.cpp new file mode 100644 index 0000000..087cf77 --- /dev/null +++ b/clang/test/CXX/basic/basic.start/basic.start.main/p2e.cpp @@ -0,0 +1,4 @@ +// RUN: clang-cc -fsyntax-only -verify %s + +inline int main() { // expected-error {{'main' is not allowed to be declared inline}} +} diff --git a/clang/test/CXX/basic/basic.start/basic.start.main/p2f.cpp b/clang/test/CXX/basic/basic.start/basic.start.main/p2f.cpp new file mode 100644 index 0000000..b7845b1 --- /dev/null +++ b/clang/test/CXX/basic/basic.start/basic.start.main/p2f.cpp @@ -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 +) { +} diff --git a/clang/test/CXX/basic/basic.start/basic.start.main/p2g.cpp b/clang/test/CXX/basic/basic.start/basic.start.main/p2g.cpp new file mode 100644 index 0000000..4cedcdb --- /dev/null +++ b/clang/test/CXX/basic/basic.start/basic.start.main/p2g.cpp @@ -0,0 +1,4 @@ +// RUN: clang-cc -fsyntax-only -verify %s + +int main(int argc, const char* const* argv) { +} diff --git a/clang/test/CodeGen/volatile.c b/clang/test/CodeGen/volatile.c index 212a0ae..87cb5ff 100644 --- a/clang/test/CodeGen/volatile.c +++ b/clang/test/CodeGen/volatile.c @@ -38,7 +38,7 @@ volatile extv4 vVE; volatile struct {int x;} aggFct(void); -void main() { +int main() { int i; // load diff --git a/clang/test/Sema/block-args.c b/clang/test/Sema/block-args.c index 27bee77..3a58735 100644 --- a/clang/test/Sema/block-args.c +++ b/clang/test/Sema/block-args.c @@ -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); diff --git a/clang/test/SemaObjC/nsobject-attribute.m b/clang/test/SemaObjC/nsobject-attribute.m index 3544cb1..c47b909 100644 --- a/clang/test/SemaObjC/nsobject-attribute.m +++ b/clang/test/SemaObjC/nsobject-attribute.m @@ -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) -- 2.7.4