From: Douglas Gregor Date: Wed, 1 Apr 2009 23:51:29 +0000 (+0000) Subject: Add some more code modification hints X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=a3a835149ed4b183e3b009a1f94a6123779d696b;p=clang Add some more code modification hints git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@68261 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/TODO.txt b/TODO.txt index df65be409d..522dcd37b3 100644 --- a/TODO.txt +++ b/TODO.txt @@ -60,5 +60,9 @@ page full of results (say, 50 lines of text). Beyond that, (1) the remaining errors are likely to be less interesting, and (2) the poor user has to scroll his terminal to find out where things went wrong. - - +//===---------------------------------------------------------------------===// +More ideas for code modification hints: + - If no member of a given name is found in a class/struct, search through the names of entities that do exist in the class and suggest the closest candidate. e.g., if I write "DS.setTypeSpecType", it would suggest "DS.SetTypeSpecType" (edit distance = 1). + - If a class member is defined out-of-line but isn't in the class declaration (and there are no close matches!), provide the option to add an in-class declaration. + - Fix-it hints for the inclusion of headers when needed for particular features (e.g., for typeid) + - Change "foo.bar" to "foo->bar" when "foo" is a pointer. diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 44317c3c6c..28a9417659 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -258,7 +258,7 @@ def err_virtual_out_of_class : Error< "'virtual' can only be specified inside the class definition">; def err_static_not_bitfield : Error<"static member %0 cannot be a bit-field">; def err_static_out_of_line : Error< - "'static' can not be specified on an out-of-line static member definition">; + "'static' can only be specified inside the class definition">; def err_typedef_not_bitfield : Error<"typedef member %0 cannot be a bit-field">; def err_not_integral_type_bitfield : Error< "bit-field %0 has non-integral type %1">; @@ -465,7 +465,7 @@ def err_ident_list_in_fn_declaration : Error< "a parameter list without types is only allowed in a function definition">; def ext_param_not_declared : Extension< "parameter %0 was not declared, defaulting to type 'int'">; -def ext_param_typedef_of_void : Extension< +def err_param_typedef_of_void : Error< "empty parameter list defined with a typedef of 'void' not allowed in C++">; def err_param_default_argument : Error< "C does not support default arguments">; @@ -580,8 +580,6 @@ def err_template_arg_list_different_arity : Error< "%select{too few|too many}0 template arguments for " "%select{class template|function template|template template parameter" "|template}1 %2">; - - def note_template_decl_here : Note<"template is declared here">; def note_member_of_template_here : Note<"member is declared here">; def err_template_arg_must_be_type : Error< @@ -604,11 +602,9 @@ def note_template_arg_refers_here_func : Note< def err_template_arg_template_params_mismatch : Error< "template template argument has different template parameters than its " "corresponding template template parameter">; - def err_template_arg_not_integral_or_enumeral : Error< "non-type template argument of type %0 must have an integral or enumeration" " type">; - def err_template_arg_not_ice : Error< "non-type template argument of type %0 is not an integral constant " "expression">; @@ -618,11 +614,9 @@ def err_template_arg_not_convertible : Error< def err_template_arg_negative : Error< "non-type template argument provides negative value '%0' for unsigned " "template parameter of type %1">; - def err_template_arg_too_large : Error< "non-type template argument value '%0' is too large for template " "parameter of type %1">; - def err_template_arg_no_ref_bind : Error< "non-type template parameter of reference type %0 cannot bind to template " "argument of type %1">; diff --git a/include/clang/Parse/Action.h b/include/clang/Parse/Action.h index d53e1fb49c..b1cb34a322 100644 --- a/include/clang/Parse/Action.h +++ b/include/clang/Parse/Action.h @@ -268,7 +268,8 @@ public: /// been parsed prior to a function definition. /// @param S The function prototype scope. /// @param D The function declarator. - virtual void ActOnFinishKNRParamDeclarations(Scope *S, Declarator &D) { + virtual void ActOnFinishKNRParamDeclarations(Scope *S, Declarator &D, + SourceLocation LocAfterDecls) { } /// ActOnStartOfFunctionDef - This is called at the start of a function diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index 5b38f60abe..495d5e51dd 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -740,7 +740,7 @@ void Parser::ParseKNRParamDeclarations(Declarator &D) { } // The actions module must verify that all arguments were declared. - Actions.ActOnFinishKNRParamDeclarations(CurScope, D); + Actions.ActOnFinishKNRParamDeclarations(CurScope, D, Tok.getLocation()); } diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index c442b84669..f9270cf64f 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -368,7 +368,8 @@ public: virtual void SetDeclDeleted(DeclPtrTy dcl, SourceLocation DelLoc); virtual DeclGroupPtrTy FinalizeDeclaratorGroup(Scope *S, DeclPtrTy *Group, unsigned NumDecls); - virtual void ActOnFinishKNRParamDeclarations(Scope *S, Declarator &D); + virtual void ActOnFinishKNRParamDeclarations(Scope *S, Declarator &D, + SourceLocation LocAfterDecls); virtual DeclPtrTy ActOnStartOfFunctionDef(Scope *S, Declarator &D); virtual DeclPtrTy ActOnStartOfFunctionDef(Scope *S, DeclPtrTy D); virtual void ActOnStartOfObjCMethodDef(Scope *S, DeclPtrTy D); diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index e02e2ee876..f67b417ddd 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -2041,7 +2041,7 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC, // typedef of void is not permitted. if (getLangOptions().CPlusPlus && Param->getType().getUnqualifiedType() != Context.VoidTy) { - Diag(Param->getLocation(), diag::ext_param_typedef_of_void); + Diag(Param->getLocation(), diag::err_param_typedef_of_void); } } else if (FTI.NumArgs > 0 && FTI.ArgInfo[0].Param != 0) { for (unsigned i = 0, e = FTI.NumArgs; i != e; ++i) @@ -2697,7 +2697,8 @@ Sema::ActOnParamDeclarator(Scope *S, Declarator &D) { return DeclPtrTy::make(New); } -void Sema::ActOnFinishKNRParamDeclarations(Scope *S, Declarator &D) { +void Sema::ActOnFinishKNRParamDeclarations(Scope *S, Declarator &D, + SourceLocation LocAfterDecls) { assert(D.getTypeObject(0).Kind == DeclaratorChunk::Function && "Not a function declarator!"); DeclaratorChunk::FunctionTypeInfo &FTI = D.getTypeObject(0).Fun; @@ -2707,8 +2708,13 @@ void Sema::ActOnFinishKNRParamDeclarations(Scope *S, Declarator &D) { if (!FTI.hasPrototype) { for (unsigned i = 0, e = FTI.NumArgs; i != e; ++i) { if (FTI.ArgInfo[i].Param == 0) { + std::string Code = "int "; + Code += FTI.ArgInfo[i].Ident->getName(); + Code += ";\n "; Diag(FTI.ArgInfo[i].IdentLoc, diag::ext_param_not_declared) - << FTI.ArgInfo[i].Ident; + << FTI.ArgInfo[i].Ident + << CodeModificationHint::CreateInsertion(LocAfterDecls, Code); + // Implicitly declare the argument as type 'int' for lack of a better // type. DeclSpec DS; @@ -3215,13 +3221,29 @@ Sema::DeclPtrTy Sema::ActOnTag(Scope *S, unsigned TagSpec, TagKind TK, // Make sure that this wasn't declared as an enum and now used as a // struct or something similar. if (PrevTagDecl->getTagKind() != Kind) { - Diag(KWLoc, diag::err_use_with_wrong_tag) << Name; + bool SafeToContinue + = (PrevTagDecl->getTagKind() != TagDecl::TK_enum && + Kind != TagDecl::TK_enum); + if (SafeToContinue) + Diag(KWLoc, diag::err_use_with_wrong_tag) + << Name + << CodeModificationHint::CreateReplacement(SourceRange(KWLoc), + PrevTagDecl->getKindName()); + else + Diag(KWLoc, diag::err_use_with_wrong_tag) << Name; Diag(PrevDecl->getLocation(), diag::note_previous_use); - // Recover by making this an anonymous redefinition. - Name = 0; - PrevDecl = 0; - Invalid = true; - } else { + + if (SafeToContinue) + Kind = PrevTagDecl->getTagKind(); + else { + // Recover by making this an anonymous redefinition. + Name = 0; + PrevDecl = 0; + Invalid = true; + } + } + + if (!Invalid) { // If this is a use, just return the declaration we found. // FIXME: In the future, return a variant or some other clue diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index ea2af68d16..c2928dc6cc 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -1307,8 +1307,9 @@ bool Sema::CheckConstructor(CXXConstructorDecl *Constructor) { QualType ParamType = Constructor->getParamDecl(0)->getType(); QualType ClassTy = Context.getTagDeclType(ClassDecl); if (Context.getCanonicalType(ParamType).getUnqualifiedType() == ClassTy) { - Diag(Constructor->getLocation(), diag::err_constructor_byvalue_arg) - << SourceRange(Constructor->getParamDecl(0)->getLocation()); + SourceLocation ParamLoc = Constructor->getParamDecl(0)->getLocation(); + Diag(ParamLoc, diag::err_constructor_byvalue_arg) + << CodeModificationHint::CreateInsertion(ParamLoc, "const &"); Invalid = true; } } diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 7a25404faa..1540f0a009 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -3402,11 +3402,25 @@ QualType Sema::CheckCompareOperands(Expr *&lex, Expr *&rex, SourceLocation Loc, // operand is null), the user probably wants strcmp. if ((isa(LHSStripped) || isa(LHSStripped)) && !RHSStripped->isNullPointerConstant(Context)) - Diag(Loc, diag::warn_stringcompare) << lex->getSourceRange(); + Diag(Loc, diag::warn_stringcompare) + << lex->getSourceRange() + << CodeModificationHint::CreateReplacement(SourceRange(Loc), ", ") + << CodeModificationHint::CreateInsertion(lex->getLocStart(), + "strcmp(") + << CodeModificationHint::CreateInsertion( + PP.getLocForEndOfToken(rex->getLocEnd()), + ") == 0"); else if ((isa(RHSStripped) || isa(RHSStripped)) && !LHSStripped->isNullPointerConstant(Context)) - Diag(Loc, diag::warn_stringcompare) << rex->getSourceRange(); + Diag(Loc, diag::warn_stringcompare) + << rex->getSourceRange() + << CodeModificationHint::CreateReplacement(SourceRange(Loc), ", ") + << CodeModificationHint::CreateInsertion(lex->getLocStart(), + "strcmp(") + << CodeModificationHint::CreateInsertion( + PP.getLocForEndOfToken(rex->getLocEnd()), + ") == 0"); } // The result of comparisons is 'bool' in C++, 'int' in C. diff --git a/lib/Sema/SemaInit.cpp b/lib/Sema/SemaInit.cpp index fbf3ae2a78..f8890c389b 100644 --- a/lib/Sema/SemaInit.cpp +++ b/lib/Sema/SemaInit.cpp @@ -540,7 +540,9 @@ void InitListChecker::CheckExplicitInitList(InitListExpr *IList, QualType &T, if (T->isScalarType()) SemaRef.Diag(IList->getLocStart(), diag::warn_braces_around_scalar_init) - << IList->getSourceRange(); + << IList->getSourceRange() + << CodeModificationHint::CreateRemoval(SourceRange(IList->getLocStart())) + << CodeModificationHint::CreateRemoval(SourceRange(IList->getLocEnd())); } void InitListChecker::CheckListElementTypes(InitListExpr *IList, diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index abc3ac08e9..d30726c766 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -462,12 +462,14 @@ Sema::ActOnClassTemplate(Scope *S, unsigned TagSpec, TagKind TK, // template declaration (7.1.5.3). RecordDecl *PrevRecordDecl = PrevClassTemplate->getTemplatedDecl(); if (PrevRecordDecl->getTagKind() != Kind) { - Diag(KWLoc, diag::err_use_with_wrong_tag) << Name; + Diag(KWLoc, diag::err_use_with_wrong_tag) + << Name + << CodeModificationHint::CreateReplacement(KWLoc, + PrevRecordDecl->getKindName()); Diag(PrevRecordDecl->getLocation(), diag::note_previous_use); - return true; + Kind = PrevRecordDecl->getTagKind(); } - // Check for redefinition of this class template. if (TK == TK_Definition) { if (TagDecl *Def = PrevRecordDecl->getDefinition(Context)) { @@ -1974,7 +1976,10 @@ Sema::ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec, TagKind TK, case DeclSpec::TST_class: Kind = TagDecl::TK_class; break; } if (ClassTemplate->getTemplatedDecl()->getTagKind() != Kind) { - Diag(KWLoc, diag::err_use_with_wrong_tag) << ClassTemplate; + Diag(KWLoc, diag::err_use_with_wrong_tag) + << ClassTemplate + << CodeModificationHint::CreateReplacement(KWLoc, + ClassTemplate->getTemplatedDecl()->getKindName()); Diag(ClassTemplate->getTemplatedDecl()->getLocation(), diag::note_previous_use); Kind = ClassTemplate->getTemplatedDecl()->getTagKind(); diff --git a/test/Sema/fixit-errors.c b/test/Sema/fixit-errors.c new file mode 100644 index 0000000000..58c60ec462 --- /dev/null +++ b/test/Sema/fixit-errors.c @@ -0,0 +1,10 @@ +// RUN: clang-cc -fsyntax-only -pedantic -verify %s + +/* This is a test of the various code modification hints that are + provided as part of warning or extension diagnostics. Eventually, + we would like to actually try to perform the suggested + modifications and compile the result to test that no warnings + remain. */ +struct s; // expected-note{{previous use is here}} + +union s *s1; // expected-error{{use of 's' with tag type that does not match previous declaration}} diff --git a/test/Sema/fixit.c b/test/Sema/fixit.c index e2443cc52f..4bdb03df6b 100644 --- a/test/Sema/fixit.c +++ b/test/Sema/fixit.c @@ -16,3 +16,14 @@ _Complex cd; struct s s0 = { y: 5 }; int array0[5] = { [3] 3 }; + +void f1(x, y) +{ +} + +int i0 = { 17 }; + +int f2(const char *my_string) { + // FIXME: terminal output isn't so good when "my_string" is shorter + return my_string == "foo"; +} diff --git a/test/SemaCXX/fixit.cpp b/test/SemaCXX/fixit.cpp index db0cd8ce18..a8859ab8cd 100644 --- a/test/SemaCXX/fixit.cpp +++ b/test/SemaCXX/fixit.cpp @@ -6,9 +6,25 @@ modifications and compile the result to test that no warnings remain. */ -struct C1 { }; +struct C1 { + virtual void f(); + static void g(); +}; struct C2 : virtual public virtual C1 { }; // expected-error{{duplicate}} -template struct CT { }; +virtual void C1::f() { } // expected-error{{'virtual' can only be specified inside the class definition}} + +static void C1::g() { } // expected-error{{'static' can only be specified inside the class definition}} + +template struct CT { }; // expected-note{{previous use is here}} CT<10 >> 2> ct; // expected-warning{{require parentheses}} + +class C3 { +public: + C3(C3, int i = 0); // expected-error{{copy constructor must pass its first argument by reference}} +}; + +struct CT<0> { }; // expected-error{{'template<>'}} + +template<> class CT<1> { }; // expected-error{{tag type}} diff --git a/test/SemaCXX/nested-name-spec.cpp b/test/SemaCXX/nested-name-spec.cpp index 07fd5a9bf8..fe92aab9a2 100644 --- a/test/SemaCXX/nested-name-spec.cpp +++ b/test/SemaCXX/nested-name-spec.cpp @@ -18,12 +18,12 @@ A::undef1::undef2 ex4; // expected-error {{no member named 'undef1'}} expected-e int A::C::Ag1() { return 0; } -static int A::C::Ag2() { return 0; } // expected-error{{'static' can not be specified on an out-of-line static member}} +static int A::C::Ag2() { return 0; } // expected-error{{'static' can}} int A::C::cx = 17; -static int A::C::cx2 = 17; // expected-error{{'static' can not be specified on an out-of-line static member}} +static int A::C::cx2 = 17; // expected-error{{'static' can}} class C2 { void m(); // expected-note{{member declaration nearly matches}}