@@ -196,11 +196,21 @@ func (l *linter) handleImport(
196196 Elaboration : helpersChecker .Elaboration ,
197197 }, nil
198198 default :
199+ // Normalize path-based imports relative to the current checker location
200+ // This ensures relative imports are resolved correctly
201+ if l .isPathLocation (importedLocation ) {
202+ importedLocation = l .normalizePathLocation (checker .Location , importedLocation )
203+ }
204+
199205 filepath , err := l .resolveImportFilepath (importedLocation , checker .Location )
200206 if err != nil {
201207 return nil , err
202208 }
203209
210+ // Use the filepath as the location for caching and SubChecker
211+ // This ensures nested imports are resolved relative to the actual file location
212+ fileLocation := common .StringLocation (filepath )
213+
204214 importedChecker , ok := l .checkers [filepath ]
205215 if ! ok {
206216 code , err := l .state .ReadFile (filepath )
@@ -219,7 +229,9 @@ func (l *linter) handleImport(
219229 }
220230 }
221231
222- importedChecker , err = checker .SubChecker (importedProgram , importedLocation )
232+ // Use the file location for the subchecker
233+ // This ensures nested imports within the imported file are resolved correctly
234+ importedChecker , err = checker .SubChecker (importedProgram , fileLocation )
223235 if err != nil {
224236 return nil , err
225237 }
@@ -237,6 +249,38 @@ func (l *linter) handleImport(
237249 }
238250}
239251
252+ // isPathLocation returns true if the location is a file path (contains .cdc)
253+ func (l * linter ) isPathLocation (location common.Location ) bool {
254+ stringLocation , ok := location .(common.StringLocation )
255+ if ! ok {
256+ return false
257+ }
258+ return strings .Contains (stringLocation .String (), ".cdc" )
259+ }
260+
261+ // normalizePathLocation normalizes a relative path import against a base location
262+ // This matches the behavior of the language server
263+ func (l * linter ) normalizePathLocation (base , relative common.Location ) common.Location {
264+ baseString , baseOk := base .(common.StringLocation )
265+ relativeString , relativeOk := relative .(common.StringLocation )
266+
267+ if ! baseOk || ! relativeOk {
268+ return relative
269+ }
270+
271+ basePath := baseString .String ()
272+ relativePath := relativeString .String ()
273+
274+ // If the relative path is absolute, return it as-is
275+ if filepath .IsAbs (relativePath ) {
276+ return relative
277+ }
278+
279+ // Join relative to the parent directory of the base
280+ normalizedPath := filepath .Join (filepath .Dir (basePath ), relativePath )
281+ return common .StringLocation (normalizedPath )
282+ }
283+
240284func (l * linter ) resolveImportFilepath (
241285 location common.Location ,
242286 parentLocation common.Location ,
@@ -246,7 +290,7 @@ func (l *linter) resolveImportFilepath(
246290) {
247291 switch location := location .(type ) {
248292 case common.StringLocation :
249- // If the location is not a cadence file try getting the code by identifier
293+ // If the location is not a cadence file, try getting the code by identifier
250294 if ! strings .Contains (location .String (), ".cdc" ) {
251295 contract , err := l .state .Contracts ().ByName (location .String ())
252296 if err != nil {
@@ -256,14 +300,9 @@ func (l *linter) resolveImportFilepath(
256300 return contract .Location , nil
257301 }
258302
259- // If the location is a cadence file, resolve relative to the parent location
260- parentPath := ""
261- if parentLocation != nil {
262- parentPath = parentLocation .String ()
263- }
264-
265- resolvedPath := filepath .Join (filepath .Dir (parentPath ), location .String ())
266- return resolvedPath , nil
303+ // If the location is a cadence file, it should already be normalized
304+ // by this point, so just return it
305+ return location .String (), nil
267306 default :
268307 return "" , fmt .Errorf ("unsupported location: %T" , location )
269308 }
0 commit comments