diff --git a/llvm/include/llvm/TableGen/Record.h b/llvm/include/llvm/TableGen/Record.h --- a/llvm/include/llvm/TableGen/Record.h +++ b/llvm/include/llvm/TableGen/Record.h @@ -1478,6 +1478,9 @@ Init *Value; bool IsUsed = false; + /// Reference locations to this record value. + SmallVector ReferenceLocs; + public: RecordVal(Init *N, RecTy *T, FieldKind K); RecordVal(Init *N, SMLoc Loc, RecTy *T, FieldKind K); @@ -1524,6 +1527,12 @@ /// Set the value and source location of the field. bool setValue(Init *V, SMLoc NewLoc); + /// Add a reference to this record value. + void addReferenceLoc(SMRange Loc) { ReferenceLocs.push_back(Loc); } + + /// Return the references of this record value. + ArrayRef getReferenceLocs() const { return ReferenceLocs; } + /// Whether this value is used. Useful for reporting warnings, for example /// when a template argument is unused. void setUsed(bool Used) { IsUsed = Used; } @@ -1556,9 +1565,11 @@ private: Init *Name; // Location where record was instantiated, followed by the location of - // multiclass prototypes used. + // multiclass prototypes used, and finally by the locations of references to + // this record. SmallVector Locs; SmallVector ForwardDeclarationLocs; + SmallVector ReferenceLocs; SmallVector TemplateArgs; SmallVector Values; SmallVector Assertions; @@ -1628,6 +1639,12 @@ return ForwardDeclarationLocs; } + /// Add a reference to this record value. + void appendReferenceLoc(SMRange Loc) { ReferenceLocs.push_back(Loc); } + + /// Return the references of this record value. + ArrayRef getReferenceLocs() const { return ReferenceLocs; } + // Update a class location when encountering a (re-)definition. void updateClassLoc(SMLoc Loc); diff --git a/llvm/lib/TableGen/TGLexer.h b/llvm/lib/TableGen/TGLexer.h --- a/llvm/lib/TableGen/TGLexer.h +++ b/llvm/lib/TableGen/TGLexer.h @@ -131,6 +131,7 @@ } SMLoc getLoc() const; + SMRange getLocRange() const; private: /// LexToken - Read the next token and return its code. diff --git a/llvm/lib/TableGen/TGLexer.cpp b/llvm/lib/TableGen/TGLexer.cpp --- a/llvm/lib/TableGen/TGLexer.cpp +++ b/llvm/lib/TableGen/TGLexer.cpp @@ -63,6 +63,10 @@ return SMLoc::getFromPointer(TokStart); } +SMRange TGLexer::getLocRange() const { + return {getLoc(), SMLoc::getFromPointer(CurPtr)}; +} + /// ReturnError - Set the error to the specified string at the specified /// location. This is defined to always return tgtok::Error. tgtok::TokKind TGLexer::ReturnError(SMLoc Loc, const Twine &Msg) { diff --git a/llvm/lib/TableGen/TGParser.h b/llvm/lib/TableGen/TGParser.h --- a/llvm/lib/TableGen/TGParser.h +++ b/llvm/lib/TableGen/TGParser.h @@ -200,7 +200,7 @@ bool AddValue(Record *TheRec, SMLoc Loc, const RecordVal &RV); bool SetValue(Record *TheRec, SMLoc Loc, Init *ValName, ArrayRef BitList, Init *V, - bool AllowSelfAssignment = false); + bool AllowSelfAssignment = false, bool OverrideDefLoc = true); bool AddSubClass(Record *Rec, SubClassReference &SubClass); bool AddSubClass(RecordsEntry &Entry, SubClassReference &SubClass); bool AddSubMultiClass(MultiClass *CurMC, @@ -244,7 +244,7 @@ SubClassReference ParseSubClassReference(Record *CurRec, bool isDefm); SubMultiClassReference ParseSubMultiClassReference(MultiClass *CurMC); - Init *ParseIDValue(Record *CurRec, StringInit *Name, SMLoc NameLoc, + Init *ParseIDValue(Record *CurRec, StringInit *Name, SMRange NameLoc, IDParseMode Mode = ParseValueMode); Init *ParseSimpleValue(Record *CurRec, RecTy *ItemType = nullptr, IDParseMode Mode = ParseValueMode); diff --git a/llvm/lib/TableGen/TGParser.cpp b/llvm/lib/TableGen/TGParser.cpp --- a/llvm/lib/TableGen/TGParser.cpp +++ b/llvm/lib/TableGen/TGParser.cpp @@ -161,7 +161,7 @@ /// Return true on error, false on success. bool TGParser::SetValue(Record *CurRec, SMLoc Loc, Init *ValName, ArrayRef BitList, Init *V, - bool AllowSelfAssignment) { + bool AllowSelfAssignment, bool OverrideDefLoc) { if (!V) return false; if (!CurRec) CurRec = &CurMultiClass->Rec; @@ -211,7 +211,7 @@ V = BitsInit::get(Records, NewBits); } - if (RV->setValue(V, Loc)) { + if (OverrideDefLoc ? RV->setValue(V, Loc) : RV->setValue(V)) { std::string InitType; if (BitsInit *BI = dyn_cast(V)) InitType = (Twine("' of type bit initializer with length ") + @@ -586,6 +586,8 @@ Lex.getCurStrVal() + "'"); else TokError(Msg); + } else { + Result->appendReferenceLoc(Lex.getLocRange()); } Lex.Lex(); @@ -867,11 +869,13 @@ } /// ParseIDValue -Init *TGParser::ParseIDValue(Record *CurRec, StringInit *Name, SMLoc NameLoc, +Init *TGParser::ParseIDValue(Record *CurRec, StringInit *Name, SMRange NameLoc, IDParseMode Mode) { if (CurRec) { - if (const RecordVal *RV = CurRec->getValue(Name)) + if (RecordVal *RV = CurRec->getValue(Name)) { + RV->addReferenceLoc(NameLoc); return VarInit::get(Name, RV->getType()); + } } if ((CurRec && CurRec->isClass()) || CurMultiClass) { @@ -887,6 +891,7 @@ RecordVal *RV = TemplateRec->getValue(TemplateArgName); assert(RV && "Template arg doesn't exist??"); RV->setUsed(true); + RV->addReferenceLoc(NameLoc); return VarInit::get(TemplateArgName, RV->getType()); } else if (Name->getValue() == "NAME") { return VarInit::get(TemplateArgName, StringRecTy::get(Records)); @@ -909,8 +914,12 @@ if (Mode == ParseNameMode) return Name; - if (Init *I = Records.getGlobal(Name->getValue())) + if (Init *I = Records.getGlobal(Name->getValue())) { + // Add a reference to the global if it's a record. + if (auto *Def = dyn_cast(I)) + Def->getDef()->appendReferenceLoc(NameLoc); return I; + } // Allow self-references of concrete defs, but delay the lookup so that we // get the correct type. @@ -918,7 +927,7 @@ CurRec->getNameInit() == Name) return UnOpInit::get(UnOpInit::CAST, Name, CurRec->getType()); - Error(NameLoc, "Variable not defined: '" + Name->getValue() + "'"); + Error(NameLoc.Start, "Variable not defined: '" + Name->getValue() + "'"); return nullptr; } @@ -2184,7 +2193,7 @@ Lex.Lex(); break; case tgtok::Id: { - SMLoc NameLoc = Lex.getLoc(); + SMRange NameLoc = Lex.getLocRange(); StringInit *Name = StringInit::get(Records, Lex.getCurStrVal()); if (Lex.Lex() != tgtok::less) // consume the Id. return ParseIDValue(CurRec, Name, NameLoc, Mode); // Value ::= IDValue @@ -2194,7 +2203,8 @@ // from the class with the template arguments, but no body. Record *Class = Records.getClass(Name->getValue()); if (!Class) { - Error(NameLoc, "Expected a class name, got '" + Name->getValue() + "'"); + Error(NameLoc.Start, + "Expected a class name, got '" + Name->getValue() + "'"); return nullptr; } @@ -2203,7 +2213,7 @@ if (ParseTemplateArgValueList(Args, CurRec, Class)) return nullptr; // Error parsing value list. - if (CheckTemplateArgValues(Args, NameLoc, Class)) + if (CheckTemplateArgValues(Args, NameLoc.Start, Class)) return nullptr; // Error checking template argument values. // Loop through the arguments that were not specified and make sure @@ -2211,14 +2221,15 @@ ArrayRef TArgs = Class->getTemplateArgs(); for (unsigned I = Args.size(), E = TArgs.size(); I < E; ++I) { RecordVal *Arg = Class->getValue(TArgs[I]); - if (!Arg->getValue()->isComplete()) - Error(NameLoc, "Value not specified for template argument '" + - TArgs[I]->getAsUnquotedString() + "' (#" + Twine(I) + - ") of parent class '" + - Class->getNameInitAsString() + "'"); - + if (!Arg->getValue()->isComplete()) { + Error(NameLoc.Start, "Value not specified for template argument '" + + TArgs[I]->getAsUnquotedString() + "' (#" + + Twine(I) + ") of parent class '" + + Class->getNameInitAsString() + "'"); + } } + Class->appendReferenceLoc(NameLoc); return VarDefInit::get(Class, Args)->Fold(); } case tgtok::l_brace: { // Value ::= '{' ValueList '}' @@ -2510,12 +2521,27 @@ TokError("expected field identifier after '.'"); return nullptr; } + SMRange FieldNameLoc = Lex.getLocRange(); StringInit *FieldName = StringInit::get(Records, Lex.getCurStrVal()); if (!Result->getFieldType(FieldName)) { TokError("Cannot access field '" + Lex.getCurStrVal() + "' of value '" + Result->getAsString() + "'"); return nullptr; } + + // Add a reference to this field if we know the record class. + if (auto *DI = dyn_cast(Result)) { + DI->getDef()->getValue(FieldName)->addReferenceLoc(FieldNameLoc); + } else if (auto *TI = dyn_cast(Result)) { + if (auto *RecTy = dyn_cast(TI->getType())) { + auto RecClasses = RecTy->getClasses(); + if (RecClasses.size() == 1) { + if (auto *RV = RecClasses[0]->getValue(FieldName)) + RV->addReferenceLoc(FieldNameLoc); + } + } + } + Result = FieldInit::get(Result, FieldName)->Fold(CurRec); Lex.Lex(); // eat field name break; @@ -2780,11 +2806,13 @@ SMLoc ValLoc = Lex.getLoc(); Init *Val = ParseValue(CurRec, Type); if (!Val || - SetValue(CurRec, ValLoc, DeclName, None, Val)) + SetValue(CurRec, ValLoc, DeclName, None, Val, + /*AllowSelfAssignment=*/false, /*OverrideDefLoc=*/false)) { // Return the name, even if an error is thrown. This is so that we can // continue to make some progress, even without the value having been // initialized. return DeclName; + } } return DeclName; @@ -3078,17 +3106,24 @@ assert(Lex.getCode() == tgtok::Def && "Unknown tok"); Lex.Lex(); // Eat the 'def' token. + // If the name of the def is an Id token, use that for the location. + // Otherwise, the name is more complex and we use the location of the 'def' + // token. + SMLoc NameLoc = Lex.getCode() == tgtok::Id ? Lex.getLoc() : DefLoc; + // Parse ObjectName and make a record for it. std::unique_ptr CurRec; Init *Name = ParseObjectName(CurMultiClass); if (!Name) return true; - if (isa(Name)) - CurRec = std::make_unique(Records.getNewAnonymousName(), DefLoc, Records, + if (isa(Name)) { + CurRec = + std::make_unique(Records.getNewAnonymousName(), DefLoc, Records, /*Anonymous=*/true); - else - CurRec = std::make_unique(Name, DefLoc, Records); + } else { + CurRec = std::make_unique(Name, NameLoc, Records); + } if (ParseObjectBody(CurRec.get())) return true; diff --git a/llvm/test/TableGen/ConstraintChecking1.td b/llvm/test/TableGen/ConstraintChecking1.td --- a/llvm/test/TableGen/ConstraintChecking1.td +++ b/llvm/test/TableGen/ConstraintChecking1.td @@ -2,5 +2,5 @@ include "ConstraintChecking.inc" -// CHECK: [[FILE]]:[[@LINE+1]]:1: error: Unrecognized constraint '$dest1 ~ $src2' in 'Foo' +// CHECK: [[FILE]]:[[@LINE+1]]:5: error: Unrecognized constraint '$dest1 ~ $src2' in 'Foo' def Foo : TestInstructionWithConstraints<"$dest1 ~ $src2">; diff --git a/llvm/test/TableGen/ConstraintChecking2.td b/llvm/test/TableGen/ConstraintChecking2.td --- a/llvm/test/TableGen/ConstraintChecking2.td +++ b/llvm/test/TableGen/ConstraintChecking2.td @@ -2,5 +2,5 @@ include "ConstraintChecking.inc" -// CHECK: [[FILE]]:[[@LINE+1]]:1: error: Illegal format for @earlyclobber constraint in 'Foo' +// CHECK: [[FILE]]:[[@LINE+1]]:5: error: Illegal format for @earlyclobber constraint in 'Foo' def Foo : TestInstructionWithConstraints<"@earlyclobber ">; diff --git a/llvm/test/TableGen/ConstraintChecking3.td b/llvm/test/TableGen/ConstraintChecking3.td --- a/llvm/test/TableGen/ConstraintChecking3.td +++ b/llvm/test/TableGen/ConstraintChecking3.td @@ -4,5 +4,5 @@ // (This is illegal because the '=' has to be surrounded by whitespace) -// CHECK: [[FILE]]:[[@LINE+1]]:1: error: Illegal format for tied-to constraint in 'Foo' +// CHECK: [[FILE]]:[[@LINE+1]]:5: error: Illegal format for tied-to constraint in 'Foo' def Foo : TestInstructionWithConstraints<"$dest1=$dest2">; diff --git a/llvm/test/TableGen/ConstraintChecking4.td b/llvm/test/TableGen/ConstraintChecking4.td --- a/llvm/test/TableGen/ConstraintChecking4.td +++ b/llvm/test/TableGen/ConstraintChecking4.td @@ -2,5 +2,5 @@ include "ConstraintChecking.inc" -// CHECK: [[FILE]]:[[@LINE+1]]:1: error: Output operands '$dest1' and '$dest2' of 'Foo' cannot be tied! +// CHECK: [[FILE]]:[[@LINE+1]]:5: error: Output operands '$dest1' and '$dest2' of 'Foo' cannot be tied! def Foo : TestInstructionWithConstraints<"$dest1 = $dest2">; diff --git a/llvm/test/TableGen/ConstraintChecking5.td b/llvm/test/TableGen/ConstraintChecking5.td --- a/llvm/test/TableGen/ConstraintChecking5.td +++ b/llvm/test/TableGen/ConstraintChecking5.td @@ -2,5 +2,5 @@ include "ConstraintChecking.inc" -// CHECK: [[FILE]]:[[@LINE+1]]:1: error: Input operands '$src1' and '$src2' of 'Foo' cannot be tied! +// CHECK: [[FILE]]:[[@LINE+1]]:5: error: Input operands '$src1' and '$src2' of 'Foo' cannot be tied! def Foo : TestInstructionWithConstraints<"$src1 = $src2">; diff --git a/llvm/test/TableGen/ConstraintChecking6.td b/llvm/test/TableGen/ConstraintChecking6.td --- a/llvm/test/TableGen/ConstraintChecking6.td +++ b/llvm/test/TableGen/ConstraintChecking6.td @@ -2,5 +2,5 @@ include "ConstraintChecking.inc" -// CHECK: [[FILE]]:[[@LINE+1]]:1: error: Operand '$dest1' of 'Foo' cannot have multiple operands tied to it! +// CHECK: [[FILE]]:[[@LINE+1]]:5: error: Operand '$dest1' of 'Foo' cannot have multiple operands tied to it! def Foo : TestInstructionWithConstraints<"$dest1 = $src1, $dest1 = $src2">; diff --git a/llvm/test/TableGen/ConstraintChecking7.td b/llvm/test/TableGen/ConstraintChecking7.td --- a/llvm/test/TableGen/ConstraintChecking7.td +++ b/llvm/test/TableGen/ConstraintChecking7.td @@ -2,5 +2,5 @@ include "ConstraintChecking.inc" -// CHECK: [[FILE]]:[[@LINE+1]]:1: error: Operand '$src1' of 'Foo' cannot have multiple constraints! +// CHECK: [[FILE]]:[[@LINE+1]]:5: error: Operand '$src1' of 'Foo' cannot have multiple constraints! def Foo : TestInstructionWithConstraints<"$dest1 = $src1, $dest2 = $src1">; diff --git a/llvm/test/TableGen/GlobalISelEmitter-setcc.td b/llvm/test/TableGen/GlobalISelEmitter-setcc.td --- a/llvm/test/TableGen/GlobalISelEmitter-setcc.td +++ b/llvm/test/TableGen/GlobalISelEmitter-setcc.td @@ -19,6 +19,6 @@ [(set GPR32:$dst, (i32 (setcc i32:$src0, i32:$src1, SETEQ)))]>; // Check there is an error if not a CondCode operand. -// ERR: [[FILE]]:[[@LINE+1]]:1: warning: Skipped pattern: Unable to handle CondCode +// ERR: [[FILE]]:[[@LINE+1]]:5: warning: Skipped pattern: Unable to handle CondCode def FCMP_NOTCC : I<(outs GPR32:$dst), (ins FPR32Op:$src0, FPR32:$src1), [(set GPR32:$dst, (i32 (setcc f32:$src0, f32:$src1, i32)))]>; diff --git a/llvm/test/TableGen/RegisterClass.td b/llvm/test/TableGen/RegisterClass.td --- a/llvm/test/TableGen/RegisterClass.td +++ b/llvm/test/TableGen/RegisterClass.td @@ -4,4 +4,4 @@ def MyTarget : Target; def R0 : Register<"r0">; -def ClassA : RegisterClass<"MyTarget", [], 32, (add R0)>; // CHECK: [[@LINE]]:1: error: RegTypes list must not be empty! +def ClassA : RegisterClass<"MyTarget", [], 32, (add R0)>; // CHECK: [[@LINE]]:5: error: RegTypes list must not be empty! diff --git a/llvm/test/TableGen/SchedModelError.td b/llvm/test/TableGen/SchedModelError.td --- a/llvm/test/TableGen/SchedModelError.td +++ b/llvm/test/TableGen/SchedModelError.td @@ -4,7 +4,7 @@ def TestTarget : Target; -// CHECK: [[FILE]]:[[@LINE+1]]:1: error: No schedule information for instruction 'TestInst' in SchedMachineModel 'TestSchedModel' +// CHECK: [[FILE]]:[[@LINE+1]]:5: error: No schedule information for instruction 'TestInst' in SchedMachineModel 'TestSchedModel' def TestInst : Instruction { let OutOperandList = (outs); let InOperandList = (ins); diff --git a/llvm/test/TableGen/generic-tables.td b/llvm/test/TableGen/generic-tables.td --- a/llvm/test/TableGen/generic-tables.td +++ b/llvm/test/TableGen/generic-tables.td @@ -154,7 +154,7 @@ } def DFoo : DEntry<"foo", 1>; -// ERROR1: [[@LINE+1]]:1: error: Record 'DBar' for table 'DTable' is missing field 'Val1' +// ERROR1: [[@LINE+1]]:5: error: Record 'DBar' for table 'DTable' is missing field 'Val1' def DBar : DEntry<"bar", ?>; def DTable : GenericTable { diff --git a/mlir/lib/Tools/tblgen-lsp-server/TableGenServer.cpp b/mlir/lib/Tools/tblgen-lsp-server/TableGenServer.cpp --- a/mlir/lib/Tools/tblgen-lsp-server/TableGenServer.cpp +++ b/mlir/lib/Tools/tblgen-lsp-server/TableGenServer.cpp @@ -202,16 +202,15 @@ // Add references to the definition. for (SMLoc loc : def.getLoc().drop_front()) insertRef(sym, lsp::convertTokenLocToRange(loc)); - - // Add references to any super classes. - for (auto &it : def.getSuperClasses()) - insertRef(getOrInsertDef(it.first), - lsp::convertTokenLocToRange(it.second.Start)); + for (SMRange loc : def.getReferenceLocs()) + insertRef(sym, loc); // Add definitions for any values. for (const llvm::RecordVal &value : def.getValues()) { auto *sym = getOrInsertDef(&value); insertRef(sym, sym->defLoc, /*isDef=*/true); + for (SMRange refLoc : value.getReferenceLocs()) + insertRef(sym, refLoc); } } }