]> granicus.if.org Git - clang/commitdiff
Add some more code modification hints
authorDouglas Gregor <dgregor@apple.com>
Wed, 1 Apr 2009 23:51:29 +0000 (23:51 +0000)
committerDouglas Gregor <dgregor@apple.com>
Wed, 1 Apr 2009 23:51:29 +0000 (23:51 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@68261 91177308-0d34-0410-b5e6-96231b3b80d8

14 files changed:
TODO.txt
include/clang/Basic/DiagnosticSemaKinds.td
include/clang/Parse/Action.h
lib/Parse/Parser.cpp
lib/Sema/Sema.h
lib/Sema/SemaDecl.cpp
lib/Sema/SemaDeclCXX.cpp
lib/Sema/SemaExpr.cpp
lib/Sema/SemaInit.cpp
lib/Sema/SemaTemplate.cpp
test/Sema/fixit-errors.c [new file with mode: 0644]
test/Sema/fixit.c
test/SemaCXX/fixit.cpp
test/SemaCXX/nested-name-spec.cpp

index df65be409daff2f05e00c4079f802167d15b28d4..522dcd37b36565979b54bc1338d55c3f4eecf0b5 100644 (file)
--- 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., <typeinfo> for typeid)
+  - Change "foo.bar" to "foo->bar" when "foo" is a pointer.
index 44317c3c6c1d50100f35c453928e15dbb3c506f1..28a9417659314a189deedb6f346a67600a75dee2 100644 (file)
@@ -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">;
index d53e1fb49c55606985b334d173fbe11d59da531b..b1cb34a322433611399ee62426183d496cac3642 100644 (file)
@@ -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
index 5b38f60abee9c93883ea26c27a01094f820dfb10..495d5e51dd5b6bb426b124b8bb564cb12347b632 100644 (file)
@@ -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());
 }
 
 
index c442b84669e62283270dabcc5e18d08dae43e98f..f9270cf64f4f8d0ec57b6e84cb5540b42cf94793 100644 (file)
@@ -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);
index e02e2ee876e714abdf337e4f134771ed964084cd..f67b417ddd1dd163cffca7c342d528a9bbdb692b 100644 (file)
@@ -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
index ea2af68d1655199b10a0020020e0a7e81f9fd13f..c2928dc6ccf5f3931f3762a68138aa1abe67baa4 100644 (file)
@@ -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;
     }
   }
index 7a25404faa4be68834f1f128ee05e6157096be68..1540f0a009226268fab13c80a0dba6528bd778d5 100644 (file)
@@ -3402,11 +3402,25 @@ QualType Sema::CheckCompareOperands(Expr *&lex, Expr *&rex, SourceLocation Loc,
     // operand is null), the user probably wants strcmp.
     if ((isa<StringLiteral>(LHSStripped) || isa<ObjCEncodeExpr>(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<StringLiteral>(RHSStripped) ||
               isa<ObjCEncodeExpr>(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.
index fbf3ae2a78525686ebafaf667a4396c1391439e0..f8890c389b56d667a11815e224b554e0529c6580 100644 (file)
@@ -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,
index abc3ac08e9fc377283f18ca89f44b00bf745aca5..d30726c766aa708a10e28f526810bd7eff0a1590 100644 (file)
@@ -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 (file)
index 0000000..58c60ec
--- /dev/null
@@ -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}}
index e2443cc52fa2c1fb9ca63d7f19e1290f268c1632..4bdb03df6b03a6735396411cd058b80ef7ac34cc 100644 (file)
@@ -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";
+}
index db0cd8ce18657d565398a01a10c6f5e2bf6ce985..a8859ab8cdd15a1d07f03dc9bf01d1299b8e16f9 100644 (file)
@@ -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<int Value> 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<int Value> 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}}
index 07fd5a9bf85f7afbdb6589b67de72ef49bc428a8..fe92aab9a263e1e5de261ee75bd619b2537a9def 100644 (file)
@@ -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}}