diff --git a/src/analysis/typepal/Collector.rsc b/src/analysis/typepal/Collector.rsc index 7384c31..764bb3b 100644 --- a/src/analysis/typepal/Collector.rsc +++ b/src/analysis/typepal/Collector.rsc @@ -16,6 +16,7 @@ module analysis::typepal::Collector Implementation of the ICollector interface; this is the API of TypePal's fact and constraint collector */ +import DateTime; import Node; import Map; import ParseTree; @@ -289,11 +290,14 @@ TModel convertTModel2LogicalLocs(TModel tm, map[str,TModel] tmodels){ return tm; } -Collector newCollector(str modelName, map[str,Tree] namedTrees, TypePalConfig config){ +Collector newCollector(str modelName, map[str,Tree] namedTrees, TypePalConfig config, datetime timestamp = $0000-01-01T00:00:00.000+00:00$){ str normalizeName(str input) { return config.normalizeName(input); } + if(!timestamp?){ + timestamp = now(); + } loc globalScope = |global-scope:///|; Defines defines = {}; @@ -320,7 +324,6 @@ Collector newCollector(str modelName, map[str,Tree] namedTrees, TypePalConfig co loc currentScope = globalScope; loc rootScope = globalScope; - for(nm <- namedTrees) scopes[getLoc(namedTrees[nm])] = globalScope; lrel[loc scope, bool lubScope, map[ScopeRole, value] scopeInfo] scopeStack = []; list[loc] lubScopeStack = []; loc currentLubScope = globalScope; @@ -335,6 +338,7 @@ Collector newCollector(str modelName, map[str,Tree] namedTrees, TypePalConfig co else if(loc ldef := def) l = ldef; else throw TypePalUsage("Argument `def` of `define` should be `Tree` or `loc`, found "); + info.timestamp = timestamp; def_tup = ; nname = normalizeName(orgId); @@ -414,7 +418,7 @@ Collector newCollector(str modelName, map[str,Tree] namedTrees, TypePalConfig co if(Tree tdef := def) l = getLoc(tdef); else if(loc ldef := def) l = ldef; else throw TypePalUsage("Argument `def` of `defineInScope` should be `Tree` or `loc`, found "); - + info.timestamp = timestamp; nname = normalizeName(orgId); if(info is defTypeLub){ throw TypePalUsage("`defLub` cannot be used in combination with `defineInScope`"); @@ -1005,10 +1009,10 @@ Collector newCollector(str modelName, map[str,Tree] namedTrees, TypePalConfig co logical2physical += tm.logical2physical; messages += tm.messages; - scopes += tm.scopes; + scopes = tm.scopes + scopes; defines += tm.defines; - facts += tm.facts; - paths += tm.paths; + facts = tm.facts + facts; + paths = tm.paths + paths; } map[loc,loc] buildLogical2physical(Defines defines){ @@ -1018,21 +1022,29 @@ Collector newCollector(str modelName, map[str,Tree] namedTrees, TypePalConfig co my_physical2logical = invertUnique(logical2physical); } catch MultipleKey(value key, value _first, value _second):{ where = loc l := key ? l : |unknown:///|; - messages += error("Mapping from physical to logical locations is not unique; remove outdated information and try again", where); + messages += error("Mapping from physical to logical locations is not unique; remove outdated information (e.g. `mvn clean`) and try again", where); return (); } for(Define def <- defines){ - logicalLoc = my_physical2logical[def.defined] ? config.createLogicalLoc(def, modelName, config.typepalPathConfig); - if(logicalLoc != def.defined){ - if(logicalLoc in my_logical2physical){ - if(my_logical2physical[logicalLoc] != def.defined){ + if(my_physical2logical[def.defined]?){ + // logical loc already created for same physical loc + logicalLoc = my_physical2logical[def.defined]; + my_logical2physical[logicalLoc] = def.defined; + } else { + // create a new logical loc, if possible + = config.createLogicalLoc(def, modelName, config.typepalPathConfig); + if(found){ + if(logicalLoc in my_logical2physical, my_logical2physical[logicalLoc] != def.defined, my_logical2physical[logicalLoc].top == def.defined.top){ + // report different physical locs in the same file with same logical loc as clone causes = [ info("Clone of ``", my_logical2physical[logicalLoc]), info("Clone of ``", def.defined) ]; messages += error("Remove code clone for ``", def.defined, causes=causes); + } else { + // add a new logical/physical loc mapping + my_logical2physical[logicalLoc] = def.defined; } } - my_logical2physical[logicalLoc] = def.defined; } } return my_logical2physical; diff --git a/src/analysis/typepal/ConfigurableScopeGraph.rsc b/src/analysis/typepal/ConfigurableScopeGraph.rsc index da8a4d9..f4d5067 100644 --- a/src/analysis/typepal/ConfigurableScopeGraph.rsc +++ b/src/analysis/typepal/ConfigurableScopeGraph.rsc @@ -21,6 +21,7 @@ module analysis::typepal::ConfigurableScopeGraph */ extend analysis::typepal::Exception; extend analysis::typepal::ISolver; +extend analysis::typepal::TModel; import IO; import Set; @@ -89,8 +90,8 @@ str reduceToURIChars(str s){ return res; } -loc defaultLogicalLoc(Define def, str _modelName, PathConfig _pcfg){ - return def.defined; // return original and don't create logical location +tuple[bool, loc] defaultLogicalLoc(Define def, str _modelName, PathConfig _pcfg){ + return ; // return original and don't create logical location } list[str] defaultSimilarNames(Use u, TModel tm) = similarNames(u, tm); @@ -153,7 +154,7 @@ data TypePalConfig( bool(loc def, TModel tm) reportUnused = defaultReportUnused, - loc (Define def, str modelName, PathConfig pcfg) createLogicalLoc = defaultLogicalLoc, + tuple[bool, loc] (Define def, str modelName, PathConfig pcfg) createLogicalLoc = defaultLogicalLoc, list[str] (Use u, TModel tm) similarNames = defaultSimilarNames, diff --git a/src/analysis/typepal/Solver.rsc b/src/analysis/typepal/Solver.rsc index ea27d5e..a0f2779 100644 --- a/src/analysis/typepal/Solver.rsc +++ b/src/analysis/typepal/Solver.rsc @@ -80,8 +80,6 @@ Solver newSolver(map[str,Tree] namedTrees, TModel tm){ bool(loc def, TModel tm) reportUnused = defaultReportUnused; - map[loc,loc] logical2physical = tm.logical2physical; - void configTypePal(TypePalConfig tc){ normalizeName = tc.normalizeName; @@ -803,8 +801,35 @@ Solver newSolver(map[str,Tree] namedTrees, TModel tm){ definedBy[u.occ] = foundDefs; newPaths += {}; } else { - causes = [ info("Definition of ``", d) | d <- foundDefs ]; - messages += error("Name `` is ambiguous", u.occ, causes=causes); + set[Define] defs = { definitions[d] | d <- foundDefs }; + set[str] md5s = { d.defInfo.md5 | d <- defs }; + if(size(md5s) == 1){ + latestDef = getOneFrom(defs); + for(Define d <- defs){ + if(d.defInfo.timestamp?){ + if(latestDef.defInfo.timestamp?){ + if(d.defInfo.timestamp == latestDef.defInfo.timestamp){ + causes = [ info("Definition of ``", fd) | loc fd <- foundDefs ]; + messages += error("Name `` is ambiguous", u.occ, causes=causes); + } else + if(d.defInfo.timestamp > latestDef.defInfo.timestamp){ + latestDef = d; + } + } else { + latestDef = d; + } + } + } + definedBy[u.occ] = {latestDef.defined}; + newPaths += {}; + } else { + println("Ambiguous "); iprintln(foundDefs); + for(d <- foundDefs){ + println(", "); + } + causes = [ info("Definition of ``", d) | d <- foundDefs ]; + messages += error("Name `` is ambiguous", u.occ, causes=causes); + } } referPaths -= {rp}; } else { @@ -1279,7 +1304,6 @@ Solver newSolver(map[str,Tree] namedTrees, TModel tm){ orgId = udef.orgId; idRole = udef.idRole; defined = udef.defined; - if(defined in logical2physical) continue; u = use(id, orgId, defined, scope, {idRole}); // turn each unused definition into a use and check for double declarations; try { diff --git a/src/analysis/typepal/TModel.rsc b/src/analysis/typepal/TModel.rsc index fa84df9..ac77a5b 100644 --- a/src/analysis/typepal/TModel.rsc +++ b/src/analysis/typepal/TModel.rsc @@ -74,9 +74,10 @@ data ReferPath alias ReferPaths = set[ReferPath]; -// Language-specific auxiliary associated with a name definition +// Language-specific auxiliary information associated with a name definition // Extended in a language-specific module - +data DefInfo(str md5 = ""); +data DefInfo(datetime timestamp = $0000-01-01T00:00:00.000+00:00$); data DefInfo = noDefInfo() ;