[TableGen] Emit a warning for unused template args
authorCullen Rhodes <cullen.rhodes@arm.com>
Wed, 3 Nov 2021 08:04:28 +0000 (08:04 +0000)
committerCullen Rhodes <cullen.rhodes@arm.com>
Wed, 3 Nov 2021 11:55:07 +0000 (11:55 +0000)
Add a warning to TableGen for unused template arguments in classes and
multiclasses, for example:

  multiclass Foo<int x> {
    def bar;
  }

  $ llvm-tblgen foo.td

  foo.td:1:20: warning: unused template argument: Foo::x
  multiclass Foo<int x> {
                     ^
A flag '--no-warn-on-unused-template-args' is added to disable the
warning. The warning is disabled for LLVM and sub-projects if
'LLVM_ENABLE_WARNINGS=OFF'.

Reviewed By: RKSimon

Differential Revision: https://reviews.llvm.org/D109359

14 files changed:
llvm/cmake/modules/TableGen.cmake
llvm/include/llvm/TableGen/Record.h
llvm/lib/TableGen/Main.cpp
llvm/lib/TableGen/Record.cpp
llvm/lib/TableGen/TGParser.cpp
llvm/lib/TableGen/TGParser.h
llvm/test/TableGen/2010-03-24-PrematureDefaults.td
llvm/test/TableGen/TemplateArgRename.td
llvm/test/TableGen/cond-subclass.td
llvm/test/TableGen/defmclass.td
llvm/test/TableGen/if.td
llvm/test/TableGen/isa.td
llvm/test/TableGen/pr8330.td
llvm/test/TableGen/warn-unused-template-arg.td [new file with mode: 0644]

index 5e9e267..442b000 100644 (file)
@@ -80,6 +80,10 @@ function(tablegen project ofn)
     set(tblgen_change_flag "--write-if-changed")
   endif()
 
+  if (NOT LLVM_ENABLE_WARNINGS)
+    list(APPEND LLVM_TABLEGEN_FLAGS "-no-warn-on-unused-template-args")
+  endif()
+
   # We need both _TABLEGEN_TARGET and _TABLEGEN_EXE in the  DEPENDS list
   # (both the target and the file) to have .inc files rebuilt on
   # a tablegen change, as cmake does not propagate file-level dependencies
index 713d937..2cf6ef1 100644 (file)
@@ -1414,6 +1414,7 @@ private:
   SMLoc Loc; // Source location of definition of name.
   PointerIntPair<RecTy *, 2, FieldKind> TyAndKind;
   Init *Value;
+  bool IsUsed = false;
 
 public:
   RecordVal(Init *N, RecTy *T, FieldKind K);
@@ -1458,6 +1459,11 @@ public:
   /// Set the value and source location of the field.
   bool setValue(Init *V, SMLoc NewLoc);
 
+  /// Whether this value is used. Useful for reporting warnings, for example
+  /// when a template argument is unused.
+  void setUsed(bool Used) { IsUsed = Used; }
+  bool isUsed() const { return IsUsed; }
+
   void dump() const;
 
   /// Print the value to an output stream, possibly with a semicolon.
@@ -1632,6 +1638,7 @@ public:
   }
 
   void checkRecordAssertions();
+  void checkUnusedTemplateArgs();
 
   bool isSubClassOf(const Record *R) const {
     for (const auto &SCPair : SuperClasses)
index 0b10246..762255b 100644 (file)
@@ -55,6 +55,10 @@ WriteIfChanged("write-if-changed", cl::desc("Only write output if it changed"));
 static cl::opt<bool>
 TimePhases("time-phases", cl::desc("Time phases of parser and backend"));
 
+static cl::opt<bool> NoWarnOnUnusedTemplateArgs(
+    "no-warn-on-unused-template-args",
+    cl::desc("Disable unused template argument warnings."));
+
 static int reportError(const char *ProgName, Twine Msg) {
   errs() << ProgName << ": " << Msg;
   errs().flush();
@@ -107,7 +111,7 @@ int llvm::TableGenMain(const char *argv0, TableGenMainFn *MainFn) {
   // it later.
   SrcMgr.setIncludeDirs(IncludeDirs);
 
-  TGParser Parser(SrcMgr, MacroNames, Records);
+  TGParser Parser(SrcMgr, MacroNames, Records, NoWarnOnUnusedTemplateArgs);
 
   if (Parser.ParseFile())
     return 1;
index aee8b85..a81014e 100644 (file)
@@ -2660,6 +2660,16 @@ void Record::checkRecordAssertions() {
   }
 }
 
+// Report a warning if the record has unused template arguments.
+void Record::checkUnusedTemplateArgs() {
+  for (const Init *TA : getTemplateArgs()) {
+    const RecordVal *Arg = getValue(TA);
+    if (!Arg->isUsed())
+      PrintWarning(Arg->getLoc(),
+                   "unused template argument: " + Twine(Arg->getName()));
+  }
+}
+
 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
 LLVM_DUMP_METHOD void RecordKeeper::dump() const { errs() << *this; }
 #endif
index ed79630..6ccca4d 100644 (file)
@@ -874,8 +874,9 @@ Init *TGParser::ParseIDValue(Record *CurRec, StringInit *Name, SMLoc NameLoc,
 
     Record *TemplateRec = CurMultiClass ? &CurMultiClass->Rec : CurRec;
     if (TemplateRec->isTemplateArg(TemplateArgName)) {
-      const RecordVal *RV = TemplateRec->getValue(TemplateArgName);
+      RecordVal *RV = TemplateRec->getValue(TemplateArgName);
       assert(RV && "Template arg doesn't exist??");
+      RV->setUsed(true);
       return VarInit::get(TemplateArgName, RV->getType());
     } else if (Name->getValue() == "NAME") {
       return VarInit::get(TemplateArgName, StringRecTy::get());
@@ -3346,7 +3347,12 @@ bool TGParser::ParseClass() {
     if (ParseTemplateArgList(CurRec))
       return true;
 
-  return ParseObjectBody(CurRec);
+  if (ParseObjectBody(CurRec))
+    return true;
+
+  if (!NoWarnOnUnusedTemplateArgs)
+    CurRec->checkUnusedTemplateArgs();
+  return false;
 }
 
 /// ParseLetList - Parse a non-empty list of assignment expressions into a list
@@ -3541,6 +3547,9 @@ bool TGParser::ParseMultiClass() {
     PopLocalScope(MulticlassScope);
   }
 
+  if (!NoWarnOnUnusedTemplateArgs)
+    CurMultiClass->Rec.checkUnusedTemplateArgs();
+
   CurMultiClass = nullptr;
   return false;
 }
index 6e3c518..00883c8 100644 (file)
@@ -160,10 +160,13 @@ class TGParser {
                       // exist.
   };
 
+  bool NoWarnOnUnusedTemplateArgs = false;
+
 public:
-  TGParser(SourceMgr &SM, ArrayRef<std::string> Macros,
-           RecordKeeper &records)
-    : Lex(SM, Macros), CurMultiClass(nullptr), Records(records) {}
+  TGParser(SourceMgr &SM, ArrayRef<std::string> Macros, RecordKeeper &records,
+           const bool NoWarnOnUnusedTemplateArgs = false)
+      : Lex(SM, Macros), CurMultiClass(nullptr), Records(records),
+        NoWarnOnUnusedTemplateArgs(NoWarnOnUnusedTemplateArgs) {}
 
   /// ParseFile - Main entrypoint for parsing a tblgen file.  These parser
   /// routines return true on error, or false on success.
index 24f6c93..ace979c 100644 (file)
@@ -1,4 +1,4 @@
-// RUN: llvm-tblgen %s | FileCheck %s
+// RUN: llvm-tblgen --no-warn-on-unused-template-args %s | FileCheck %s
 // XFAIL: vg_leak
 
 class A<int k, bits<2> x = 1> {
index 654b86d..c5c24ce 100644 (file)
@@ -1,4 +1,4 @@
-// RUN: llvm-tblgen %s
+// RUN: llvm-tblgen --no-warn-on-unused-template-args %s
 // XFAIL: vg_leak
 
 // Make sure there is no collision between XX and XX.
index 9f6f6e2..5f31bf1 100644 (file)
@@ -1,6 +1,6 @@
 // Check that !cond with operands of different subtypes can
 // initialize a supertype variable.
-// RUN: llvm-tblgen %s | FileCheck %s
+// RUN: llvm-tblgen --no-warn-on-unused-template-args %s | FileCheck %s
 // XFAIL: vg_leak
 
 class E<int dummy> {}
index 80f03b3..2a62184 100644 (file)
@@ -1,4 +1,4 @@
-// RUN: llvm-tblgen %s | FileCheck %s
+// RUN: llvm-tblgen --no-warn-on-unused-template-args %s | FileCheck %s
 // XFAIL: vg_leak
 
 class XD { bits<4> Prefix = 11; }
index b2ba89c..cd8a8e7 100644 (file)
@@ -1,4 +1,4 @@
-// RUN: llvm-tblgen %s | FileCheck %s
+// RUN: llvm-tblgen --no-warn-on-unused-template-args %s | FileCheck %s
 // XFAIL: vg_leak
 
 // Support for an `!if' operator as part of a `let' statement.
index cfaacb0..e4095fb 100644 (file)
@@ -1,4 +1,4 @@
-// RUN: llvm-tblgen %s | FileCheck %s
+// RUN: llvm-tblgen --no-warn-on-unused-template-args %s | FileCheck %s
 // XFAIL: vg_leak
 
 // CHECK: --- Defs ---
index 7779b63..ceabbc5 100644 (file)
@@ -1,4 +1,4 @@
-// RUN: llvm-tblgen %s | FileCheck %s
+// RUN: llvm-tblgen --no-warn-on-unused-template-args %s | FileCheck %s
 // XFAIL: vg_leak
 
 class Or4<bits<8> Val> {
diff --git a/llvm/test/TableGen/warn-unused-template-arg.td b/llvm/test/TableGen/warn-unused-template-arg.td
new file mode 100644 (file)
index 0000000..5f76e82
--- /dev/null
@@ -0,0 +1,25 @@
+// RUN: llvm-tblgen %s 2>&1 | FileCheck %s
+// RUN: llvm-tblgen --no-warn-on-unused-template-args %s 2>&1 | FileCheck %s --check-prefix=CHECK-DISABLED
+
+class UnusedClassArg<int foo> {}
+
+// CHECK: warning: unused template argument: UnusedClassArg:foo
+// CHECK-NEXT: class UnusedClassArg<int foo> {}
+// CHECK-NEXT:                          ^
+
+multiclass UnusedMultiClassArg<int foo> {
+  def bar;
+}
+
+defm : UnusedMultiClassArg<1>;
+
+// CHECK: warning: unused template argument: UnusedMultiClassArg::foo
+// CHECK-NEXT: multiclass UnusedMultiClassArg<int foo> {
+// CHECK-NEXT:                                    ^
+
+class NoWarning<int b> {
+  int a = b;
+}
+
+// CHECK-NOT: warning: unused template argument: NoWarning:b
+// CHECK-DISABLED-NOT: warning