Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions shared/src/main/scala/io/kaitai/struct/format/ClassSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -253,9 +253,19 @@ object ClassSpec {
private def checkDupId(prevAttrOpt: Option[MemberSpec], id: String, nowAttr: YAMLPath): Unit = {
prevAttrOpt match {
case Some(prevAttr) =>
// Report error at position where referenced param / attribute / instance is defined.
// Add `id` for attributes in `seq` and `params`, do not add for instances
val path = nowAttr match {
case _: InstanceSpec => nowAttr.path
case _ => nowAttr.path :+ "id"
}
val prevPath = prevAttr match {
case _: InstanceSpec => prevAttr.path
case _ => prevAttr.path :+ "id"
}
throw KSYParseError.withText(
s"duplicate attribute ID '$id', previously defined at /${prevAttr.pathStr}",
nowAttr.path
s"duplicate attribute ID '$id', previously defined at /${prevPath.mkString("/")}",
path
)
case None =>
// no dups, ok
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ object MetaSpec {
)
}
if (ver > KSVersion.current)
throw KSYParseError.incompatibleVersion(ver, KSVersion.current, path ++ List("ks-version"))
throw KSYParseError.incompatibleVersion(ver, KSVersion.current, path :+ "ks-version")
}

val endian: Option[Endianness] = Endianness.fromYaml(srcMap.get("endian"), path)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,27 +41,49 @@ class ResolveTypes(specs: ClassSpecs, topClass: ClassSpec, opaqueTypes: Boolean)
}

def resolveUserTypeForMember(curClass: ClassSpec, attr: MemberSpec): Iterable[CompilationProblem] =
resolveUserType(curClass, attr.dataType, attr.path)
resolveUserType(curClass, attr.dataType, attr.path, None)

def resolveUserType(curClass: ClassSpec, dataType: DataType, path: List[String]): Iterable[CompilationProblem] = {
/**
* Resolves the `dataType` (thing referenced in the `type` property in YAML) of some attribute
* defined in `curClass`.
*
* @param curClass The user-defined type (= class) with attribute whose type is being resolved
* @param dataType The type of the attribute being resolved
* @param path YAML path to the attribute where diagnostics should be reported
* @param caseExpr Contains the specific case name (actually an expresion) if the attribute type
* is switchable type (defined by `switch-on`)
*/
def resolveUserType(
curClass: ClassSpec,
dataType: DataType,
path: List[String],
caseExpr: Option[String],
): Iterable[CompilationProblem] = {
dataType match {
case ut: UserType =>
val (resClassSpec, problems) = resolveUserType(curClass, ut.name, path ++ List("type"))
val (resClassSpec, problems) = resolveUserType(
curClass,
ut.name,
caseExpr match {
case Some(case_) => path ++ List("type", "cases", case_)
case None => path :+ "type"
}
)
ut.classSpec = resClassSpec
problems
case et: EnumType =>
et.enumSpec = resolveEnumSpec(curClass, et.name)
if (et.enumSpec.isEmpty) {
Some(EnumNotFoundErr(et.name, curClass, path ++ List("enum")))
Some(EnumNotFoundErr(et.name, curClass, path :+ "enum"))
} else {
None
}
case st: SwitchType =>
st.cases.flatMap { case (caseName, ut) =>
resolveUserType(curClass, ut, path ++ List("type", "cases", caseName.toString))
resolveUserType(curClass, ut, path, Some(caseName.toString))
}
case at: ArrayType =>
resolveUserType(curClass, at.elType, path)
resolveUserType(curClass, at.elType, path, caseExpr)
case _ =>
// not a user type, nothing to resolve
None
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,33 +39,55 @@ class StyleCheckIds(specs: ClassSpecs) extends PrecompileStep {
}
}

/**
* @param spec The user defined type (= class) in which `attr` is defined
* @param attr Attribute that references an attribute with a potentially non-conformant name
* (for example, `content`. Its `size` property should refers to `len_content` attribute)
*/
def getSizeRefProblem(spec: ClassSpec, attr: MemberSpec): Option[CompilationProblem] = {
getSizeReference(spec, attr.dataType).flatMap(sizeRefAttr => {
val existingName = sizeRefAttr.id.humanReadable
val goodName = s"len_${attr.id.humanReadable}"
if (existingName != goodName) {
// Report error at position where referenced attribute is defined.
// Add `id` for attributes in `seq`, do not add for instances
val path = sizeRefAttr match {
case _: InstanceSpec => sizeRefAttr.path
case _ => sizeRefAttr.path :+ "id"
}
Some(StyleWarningSizeLen(
goodName,
existingName,
attr.id.humanReadable,
ProblemCoords(path = Some(sizeRefAttr.path ++ List("id")))
ProblemCoords(path = Some(path))
))
} else {
None
}
})
}

/**
* @param spec The user defined type (= class) in which `attr` is defined
* @param attr Attribute that references an attribute with a potentially non-conformant name
* (for example, `content`. Its `repeat-expr` property should refers to `num_content` attribute)
*/
def getRepeatExprRefProblem(spec: ClassSpec, attr: AttrLikeSpec): Option[CompilationProblem] = {
getRepeatExprReference(spec, attr).flatMap(repeatExprRefAttr => {
val existingName = repeatExprRefAttr.id.humanReadable
val goodName = s"num_${attr.id.humanReadable}"
if (existingName != goodName) {
// Report error at position where referenced attribute is defined.
// Add `id` for attributes in `seq`, do not add for instances
val path = repeatExprRefAttr match {
case _: InstanceSpec => repeatExprRefAttr.path
case _ => repeatExprRefAttr.path :+ "id"
}
Some(StyleWarningRepeatExprNum(
goodName,
existingName,
attr.id.humanReadable,
ProblemCoords(path = Some(repeatExprRefAttr.path ++ List("id")))
ProblemCoords(path = Some(path))
))
} else {
None
Expand Down
Loading