diff --git a/include/clang/Basic/DiagnosticParseKinds.td b/include/clang/Basic/DiagnosticParseKinds.td index e4e339aefc..218a6a31da 100644 --- a/include/clang/Basic/DiagnosticParseKinds.td +++ b/include/clang/Basic/DiagnosticParseKinds.td @@ -213,6 +213,12 @@ def err_expected_semi_after_static_assert : Error< "expected ';' after static_assert">; def err_expected_semi_for : Error<"expected ';' in 'for' statement specifier">; def err_expected_colon_after : Error<"expected ':' after %0">; +def missing_selector_name : Warning< + "parameter name used as selector" + " may result in incomplete method selector name">, + InGroup>; +def note_missing_argument_name : Note< + "did you mean to use %0 as the selector name instead of %1">; def err_label_end_of_compound_statement : Error< "label at end of compound statement: expected statement">; def err_address_of_label_outside_fn : Error< diff --git a/lib/Parse/ParseObjc.cpp b/lib/Parse/ParseObjc.cpp index 1465a730e4..48c5efac6b 100644 --- a/lib/Parse/ParseObjc.cpp +++ b/lib/Parse/ParseObjc.cpp @@ -18,6 +18,7 @@ #include "clang/Sema/PrettyDeclStackTrace.h" #include "clang/Sema/Scope.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringExtras.h" using namespace clang; @@ -1031,7 +1032,7 @@ Decl *Parser::ParseObjCMethodDecl(SourceLocation mLoc, Scope::FunctionPrototypeScope|Scope::DeclScope); AttributePool allParamAttrs(AttrFactory); - + bool warnSelectorName = false; while (1) { ParsedAttributes paramAttrs(AttrFactory); Sema::ObjCArgInfo ArgInfo; @@ -1102,6 +1103,13 @@ Decl *Parser::ParseObjCMethodDecl(SourceLocation mLoc, SelIdent = ParseObjCSelectorPiece(selLoc); if (!SelIdent && Tok.isNot(tok::colon)) break; + if (MethodDefinition && !SelIdent) { + SourceLocation ColonLoc = Tok.getLocation(); + if (PP.getLocForEndOfToken(ArgInfo.NameLoc) == ColonLoc) { + warnSelectorName = true; + Diag(ArgInfo.NameLoc, diag::missing_selector_name); + } + } // We have a selector or a colon, continue parsing. } @@ -1142,6 +1150,22 @@ Decl *Parser::ParseObjCMethodDecl(SourceLocation mLoc, Selector Sel = PP.getSelectorTable().getSelector(KeyIdents.size(), &KeyIdents[0]); + if (warnSelectorName) { + SmallVector DiagKeyIdents; + for (unsigned i = 0, size = KeyIdents.size(); i < size; i++) + if (KeyIdents[i]) + DiagKeyIdents.push_back(KeyIdents[i]); + else { + std::string name = "Name"; + name += llvm::utostr(i+1); + IdentifierInfo *NamedMissingId = &PP.getIdentifierTable().get(name); + DiagKeyIdents.push_back(NamedMissingId); + } + Selector NewSel = + PP.getSelectorTable().getSelector(DiagKeyIdents.size(), &DiagKeyIdents[0]); + Diag(mLoc, diag::note_missing_argument_name) + << NewSel.getAsString() << Sel.getAsString(); + } Decl *Result = Actions.ActOnMethodDeclaration(getCurScope(), mLoc, Tok.getLocation(), mType, DSRet, ReturnType, diff --git a/test/SemaObjC/unused.m b/test/SemaObjC/unused.m index efaf9c8f3e..23d7e8cbe5 100644 --- a/test/SemaObjC/unused.m +++ b/test/SemaObjC/unused.m @@ -29,8 +29,9 @@ void test2() { @end @implementation foo -- (int) meth: (int)x: -(int)y: // expected-warning{{unused}} +- (int) meth: (int)x: // expected-warning {{parameter name used as selector may result in incomplete method selector name}} \ + // expected-note {{did you mean to use meth:Name2:Name3: as the selector name instead of meth:::}} +(int)y: // expected-warning{{unused}} expected-warning {{parameter name used as selector may result in incomplete method selector name}} (int) __attribute__((unused))z { return x; } @end diff --git a/test/SemaObjC/warning-missing-selector-name.m b/test/SemaObjC/warning-missing-selector-name.m new file mode 100644 index 0000000000..e4bde0b746 --- /dev/null +++ b/test/SemaObjC/warning-missing-selector-name.m @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -Wno-objc-root-class %s +// RUN: %clang_cc1 -x objective-c++ -fsyntax-only -verify -Wno-objc-root-class -Wmissing-argument-name-in-selector %s +// rdar://12263549 + +@interface Super @end +@interface INTF : Super +-(void) Name1:(id)Arg1 Name2:(id)Arg2; // Name1:Name2: +-(void) Name1:(id) Name2:(id)Arg2; +-(void) Name1:(id)Arg1 Name2:(id)Arg2 Name3:(id)Arg3; // Name1:Name2:Name3: +-(void) Name1:(id)Arg1 Name2:(id) Name3:(id)Arg3; +@end + +@implementation INTF +-(void) Name1:(id)Arg1 Name2:(id)Arg2{} +-(void) Name1:(id) Name2:(id)Arg2 {} // expected-warning {{parameter name used as selector may result in incomplete method selector name}} \ + // expected-note {{did you mean to use Name1:Name2: as the selector name instead of Name1::}} +-(void) Name1:(id)Arg1 Name2:(id)Arg2 Name3:(id)Arg3 {} +-(void) Name1:(id)Arg1 Name2:(id) Name3:(id)Arg3 {} // expected-warning {{parameter name used as selector may result in incomplete method selector name}} \ + // expected-note {{did you mean to use Name1:Name2:Name3: as the selector name instead of Name1:Name2::}} +@end