refchecks.scala
来自「JAVA 语言的函数式编程扩展」· SCALA 代码 · 共 841 行 · 第 1/3 页
SCALA
841 行
// 4. Check that every defined member with an `override' modifier overrides some other member. for (val member <- clazz.info.decls.toList) if ((member hasFlag (OVERRIDE | ABSOVERRIDE)) && (clazz.info.baseClasses.tail forall { bc => member.matchingSymbol(bc, clazz.thisType) == NoSymbol })) { // for (val bc <- clazz.info.baseClasses.tail) Console.println("" + bc + " has " + bc.info.decl(member.name) + ":" + bc.info.decl(member.name).tpe);//DEBUG unit.error(member.pos, member.toString() + " overrides nothing"); member resetFlag OVERRIDE } } // Basetype Checking -------------------------------------------------------- /** <ol> * <li> <!-- 1 --> * Check that later type instances in the base-type sequence * are subtypes of earlier type instances of the same mixin. * </li> * <li> <!-- 2 --> * Check that inner classes do not inherit from Annotation * </li> * </ol> */ private def validateBaseTypes(clazz: Symbol) { val seenTypes = new Array[Type](clazz.info.closure.length) /** validate all base types of a class in reverse linear order. */ def validateType(tp: Type) { val baseClass = tp.typeSymbol if (baseClass.isClass) { val index = clazz.info.closurePos(baseClass) if (index >= 0) { if (seenTypes(index) ne null) { if (!(seenTypes(index) <:< tp)) { unit.error(clazz.pos, "illegal inheritance;\n " + clazz + " inherits different type instances of " + baseClass + ":\n" + tp + " and " + seenTypes(index)); } } else { seenTypes(index) = tp // check that inner classes do not inherit from Annotation if (baseClass == ClassfileAnnotationClass) if (!clazz.owner.isPackageClass) unit.error(clazz.pos, "inner classes cannot be classfile annotations") } tp.parents foreach validateType } } } validateType(clazz.tpe) } // Variance Checking -------------------------------------------------------- private val ContraVariance = -1 private val NoVariance = 0 private val CoVariance = 1 private val AnyVariance = 2 private val escapedPrivateLocals = new HashSet[Symbol] val varianceValidator = new Traverser { private def validateVariance(base: Symbol) { def varianceString(variance: Int): String = if (variance == 1) "covariant" else if (variance == -1) "contravariant" else "invariant"; def relativeVariance(tvar: Symbol): Int = { val clazz = tvar.owner var sym = base var state = CoVariance while (sym != clazz && state != AnyVariance) { //Console.println("flip: " + sym + " " + sym.isParameter());//DEBUG if ((sym hasFlag PARAM) && !sym.owner.isConstructor && !sym.owner.isCaseApplyOrUnapply && !(tvar.isTypeParameterOrSkolem && sym.isTypeParameterOrSkolem && tvar.owner == sym.owner)) state = -state; else if (!sym.owner.isClass || ((sym.isPrivateLocal || sym.isProtectedLocal) && !(escapedPrivateLocals contains sym))) state = AnyVariance else if (sym.isAliasType) state = NoVariance sym = sym.owner } state } def validateVariance(tp: Type, variance: Int): Unit = tp match { case ErrorType => ; case WildcardType => ; case NoType => ; case NoPrefix => ; case ThisType(_) => ; case ConstantType(_) => ; case DeBruijnIndex(_, _) => ; case SingleType(pre, sym) => validateVariance(pre, variance) case TypeRef(pre, sym, args) => if (sym.variance != NoVariance) { val v = relativeVariance(sym); if (v != AnyVariance && sym.variance != v * variance) { //Console.println("relativeVariance(" + base + "," + sym + ") = " + v);//DEBUG unit.error(base.pos, varianceString(sym.variance) + " " + sym + " occurs in " + varianceString(v * variance) + " position in type " + base.info + " of " + base); } } validateVariance(pre, variance) validateVarianceArgs(args, variance, sym.typeParams) //@M for higher-kinded typeref, args.isEmpty // However, these args respect variances by construction anyway // -- the interesting case is in type application, see checkKindBounds in Infer case ClassInfoType(parents, decls, symbol) => validateVariances(parents, variance) case RefinedType(parents, decls) => validateVariances(parents, variance) case TypeBounds(lo, hi) => validateVariance(lo, -variance) validateVariance(hi, variance) case MethodType(formals, result) => validateVariance(result, variance) case PolyType(tparams, result) => // type parameters will be validated separately, because they are defined explicitly. validateVariance(result, variance) case ExistentialType(tparams, result) => validateVariances(tparams map (_.info), variance) validateVariance(result, variance) case AnnotatedType(attribs, tp, selfsym) => validateVariance(tp, variance) } def validateVariances(tps: List[Type], variance: Int) { tps foreach (tp => validateVariance(tp, variance)) } def validateVarianceArgs(tps: List[Type], variance: Int, tparams: List[Symbol]) { (tps zip tparams) foreach { case (tp, tparam) => validateVariance(tp, variance * tparam.variance) } } validateVariance(base.info, CoVariance) } override def traverse(tree: Tree) { tree match { case ClassDef(_, _, _, _) | TypeDef(_, _, _, _) => validateVariance(tree.symbol) super.traverse(tree) // ModuleDefs need not be considered because they have been eliminated already case ValDef(_, _, _, _) => validateVariance(tree.symbol) case DefDef(_, _, tparams, vparamss, tpt, rhs) => validateVariance(tree.symbol) traverseTrees(tparams); traverseTreess(vparamss) case Template(_, _, _) => super.traverse(tree) case _ => } } }// Forward reference checking --------------------------------------------------- class LevelInfo(val outer: LevelInfo) { val scope: Scope = if (outer eq null) newScope else newScope(outer.scope) var maxindex: Int = Math.MIN_INT var refpos: Position = _ var refsym: Symbol = _ } private var currentLevel: LevelInfo = null private val symIndex = new HashMap[Symbol, Int] private def pushLevel() { currentLevel = new LevelInfo(currentLevel) } private def popLevel() { currentLevel = currentLevel.outer } private def enterSyms(stats: List[Tree]) { var index = -1 for (val stat <- stats) { index = index + 1; stat match { case ClassDef(_, _, _, _) | DefDef(_, _, _, _, _, _) | ModuleDef(_, _, _) | ValDef(_, _, _, _) => assert(stat.symbol != NoSymbol, stat);//debug if (stat.symbol.isLocal) { currentLevel.scope.enter(newScopeEntry(stat.symbol, currentLevel.scope)); symIndex(stat.symbol) = index; } case _ => } } } private def enterReference(pos: Position, sym: Symbol) { if (sym.isLocal) { val e = currentLevel.scope.lookupEntry(sym.name) if ((e ne null) && sym == e.sym) { var l = currentLevel while (l.scope != e.owner) l = l.outer; val symindex = symIndex(sym) if (l.maxindex < symindex) { l.refpos = pos l.refsym = sym l.maxindex = symindex } } } }// Comparison checking ------------------------------------------------------- object normalizeAll extends TypeMap { def apply(tp: Type) = mapOver(tp).normalize } def checkSensible(pos: Position, fn: Tree, args: List[Tree]) = fn match { case Select(qual, name) if (args.length == 1) => def isNew(tree: Tree) = tree match { case Function(_, _) | Apply(Select(New(_), nme.CONSTRUCTOR), _) => true case _ => false } name match { case nme.EQ | nme.NE | nme.LT | nme.GT | nme.LE | nme.GE => def underlyingClass(tp: Type): Symbol = { var sym = tp.widen.typeSymbol while (sym.isAbstractType) sym = sym.info.bounds.hi.widen.typeSymbol sym } val formal = underlyingClass(fn.tpe.paramTypes.head) val actual = underlyingClass(args.head.tpe) val receiver = underlyingClass(qual.tpe) def nonSensibleWarning(what: String, alwaysEqual: Boolean) = unit.warning(pos, "comparing "+what+" using `"+name.decode+"' will always yield "+ (alwaysEqual == (name == nme.EQ || name == nme.LE || name == nme.GE))) def nonSensible(pre: String, alwaysEqual: Boolean) = nonSensibleWarning(pre+"values of types "+normalizeAll(qual.tpe.widen)+" and "+normalizeAll(args.head.tpe.widen), alwaysEqual) // @MAT normalize for consistency in error message, otherwise part is normalized due to use of `typeSymbol', but the rest isn't def hasObjectEquals = receiver.info.member(nme.equals_) == Object_equals if (formal == UnitClass && actual == UnitClass) nonSensible("", true) else if ((receiver == BooleanClass || receiver == UnitClass) && !(receiver isSubClass actual)) nonSensible("", false) else if (isNumericValueClass(receiver) && !isNumericValueClass(actual) && !(forMSIL || forCLDC|| (actual isSubClass BoxedNumberClass)) && !(receiver isSubClass actual)) nonSensible("", false) else if ((receiver hasFlag FINAL) && hasObjectEquals && !isValueClass(receiver) && !(receiver isSubClass actual) && receiver != AllRefClass && actual != AllRefClass && (name == nme.EQ || name == nme.LE)) nonSensible("non-null ", false) else if ((isNew(qual) || isNew(args.head)) && hasObjectEquals) nonSensibleWarning("a fresh object", false) case _ => } case _ => }// Transformation ------------------------------------------------------------ /* Convert a reference to a case factory of type `tpe' to a new of the class it produces. */ def toConstructor(pos: Position, tpe: Type): Tree = { var rtpe = tpe.finalResultType assert(rtpe.typeSymbol hasFlag CASE, tpe); localTyper.typedOperator { atPos(pos) { Select(New(TypeTree(rtpe)), rtpe.typeSymbol.primaryConstructor) } } }
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?