Repository: rclone/rclone
Pull request #6228 (2022-06-08 21:23:08)

Fix staticcheck linting problems
#### What is the purpose of this change?

Running the staticcheck lint tool (v0.3.2) and fixing reported "problems". With a clean master it reports more than 300 problems. This PR removes more than 100 of these. After this PR and also the separate #6237 which fixes "Error strings should not be capitalized" reported issues, the total number of reported problems is reduced to something close to 60.

Example of frequently reported problems are:
- Avoid unnecessary use of fmt.Sprintf (S1039)
- Use unconditional TrimPrefix instead of conditional with HasPrefix
- Use strings.EqualFold instead of comparing results of ToLower
   - Note that the result is not 100% guaranteed identical due to Unicode case-folding rules, and therefore there have been some debate on this lint rule. Not very likely to hit any real cases where there is difference? And perhaps EqualFold is closer to what is expected anyway? Primary reasons to use EqualFold is that it supposedly outperforms the ToLower comparison by orders of magnitude, and also is more readable (in my opinion).
- Merge variable declaration and assignment (S1021)
- Replace x.Sub(time.Now()) with time.Until(x) (S1024)

Remaining lint problems:
- Error strings should not be capitalized (ST1005)
    - Done in separate PR: #6237
- Some Linux mount specifics fixed in separate PRs #6238 and #6239
- The rest is mostly deprecations and unused functions.
- Some "assigning the result of this type assertion to a variable (switch e := e.(type)) could eliminate type assertions in switch cases (S1034)".
- One issue in lib\http\auth\basic.go can be worth looking into: "this value of r is never used (SA4006)" and "WithContext is a pure function but its return value is ignored (SA4017)" on line `r = r.WithContext(context.WithValue(r.Context(), ContextAuthKey, value))`
- Another issue in vfs\vfs.go: "ineffective break statement. Did you mean to break out of the outer loop? (SA4011)", on the break statement in `case <-tick.C:`

#### Was the change discussed in an issue or in the forum before?

<!--
Link issues and relevant forum posts here.
-->

#### Checklist

- [X] I have read the [contribution guidelines](https://github.com/rclone/rclone/blob/master/CONTRIBUTING.md#submitting-a-new-feature-or-bug-fix).
- [ ] I have added tests for all changes in this PR if appropriate.
- [ ] I have added documentation for the changes if appropriate.
- [X] All commit messages are in [house style](https://github.com/rclone/rclone/blob/master/CONTRIBUTING.md#commit-messages).
- [X] I'm done, this Pull Request is ready for review :-)



Patch:
diff --git a/backend/amazonclouddrive/amazonclouddrive.go b/backend/amazonclouddrive/amazonclouddrive.go
index 58a14427b..e5f5512ed 100644
--- a/backend/amazonclouddrive/amazonclouddrive.go
+++ b/backend/amazonclouddrive/amazonclouddrive.go
@@ -435,7 +435,7 @@ func (f *Fs) listAll(ctx context.Context, dirID string, title string, directorie
 		query += " AND kind:" + folderKind
 	} else if filesOnly {
 		query += " AND kind:" + fileKind
-	} else {
+		//} else {
 		// FIXME none of these work
 		//query += " AND kind:(" + fileKind + " OR " + folderKind + ")"
 		//query += " AND (kind:" + fileKind + " OR kind:" + folderKind + ")"
diff --git a/backend/b2/b2.go b/backend/b2/b2.go
index ba3e03eb7..322aa50a5 100644
--- a/backend/b2/b2.go
+++ b/backend/b2/b2.go
@@ -280,7 +280,7 @@ func (f *Fs) Root() string {
 // String converts this Fs to a string
 func (f *Fs) String() string {
 	if f.rootBucket == "" {
-		return fmt.Sprintf("B2 root")
+		return "B2 root"
 	}
 	if f.rootDirectory == "" {
 		return fmt.Sprintf("B2 bucket %s", f.rootBucket)
@@ -1205,10 +1205,7 @@ func (f *Fs) purge(ctx context.Context, dir string, oldOnly bool) error {
 		}
 	}
 	var isUnfinishedUploadStale = func(timestamp api.Timestamp) bool {
-		if time.Since(time.Time(timestamp)).Hours() > 24 {
-			return true
-		}
-		return false
+		return time.Since(time.Time(timestamp)).Hours() > 24
 	}
 
 	// Delete Config.Transfers in parallel
@@ -1485,13 +1482,9 @@ func (o *Object) Size() int64 {
 //
 // Remove unverified prefix - see https://www.backblaze.com/b2/docs/uploading.html
 // Some tools (e.g. Cyberduck) use this
-func cleanSHA1(sha1 string) (out string) {
-	out = strings.ToLower(sha1)
+func cleanSHA1(sha1 string) string {
 	const unverified = "unverified:"
-	if strings.HasPrefix(out, unverified) {
-		out = out[len(unverified):]
-	}
-	return out
+	return strings.TrimPrefix(strings.ToLower(sha1), unverified)
 }
 
 // decodeMetaDataRaw sets the metadata from the data passed in
diff --git a/backend/box/box.go b/backend/box/box.go
index bde259cea..55d8a4030 100644
--- a/backend/box/box.go
+++ b/backend/box/box.go
@@ -897,7 +897,7 @@ func (f *Fs) Copy(ctx context.Context, src fs.Object, remote string) (fs.Object,
 
 	srcPath := srcObj.fs.rootSlash() + srcObj.remote
 	dstPath := f.rootSlash() + remote
-	if strings.ToLower(srcPath) == strings.ToLower(dstPath) {
+	if strings.EqualFold(srcPath, dstPath) {
 		return nil, fmt.Errorf("can't copy %q -> %q as are same name when lowercase", srcPath, dstPath)
 	}
 
diff --git a/backend/cache/storage_memory.go b/backend/cache/storage_memory.go
index 8e8a360fc..25ac3bb62 100644
--- a/backend/cache/storage_memory.go
+++ b/backend/cache/storage_memory.go
@@ -76,10 +76,7 @@ func (m *Memory) CleanChunksByAge(chunkAge time.Duration) {
 
 // CleanChunksByNeed will cleanup chunks after the FS passes a specific chunk
 func (m *Memory) CleanChunksByNeed(offset int64) {
-	var items map[string]cache.Item
-
-	items = m.db.Items()
-	for key := range items {
+	for key := range m.db.Items() {
 		sepIdx := strings.LastIndex(key, "-")
 		keyOffset, err := strconv.ParseInt(key[sepIdx+1:], 10, 64)
 		if err != nil {
diff --git a/backend/cache/storage_persistent.go b/backend/cache/storage_persistent.go
index 89af35f87..0de9a3963 100644
--- a/backend/cache/storage_persistent.go
+++ b/backend/cache/storage_persistent.go
@@ -456,10 +456,7 @@ func (b *Persistent) HasEntry(remote string) bool {
 
 		return fmt.Errorf("couldn't find object (%v)", remote)
 	})
-	if err == nil {
-		return true
-	}
-	return false
+	return err == nil
 }
 
 // HasChunk confirms the existence of a single chunk of an object
diff --git a/backend/combine/combine.go b/backend/combine/combine.go
index c3c5ff38a..934ed76f4 100644
--- a/backend/combine/combine.go
+++ b/backend/combine/combine.go
@@ -195,7 +195,7 @@ func NewFs(ctx context.Context, name, root string, m configmap.Mapper) (outFs fs
 			if remote == "" {
 				return fmt.Errorf("empty remote in upstream definition %q", upstream)
 			}
-			if strings.IndexRune(dir, '/') >= 0 {
+			if strings.ContainsRune(dir, '/') {
 				return fmt.Errorf("dirs can't contain / (yet): %q", dir)
 			}
 			u, err := f.newUpstream(gCtx, dir, remote)
diff --git a/backend/compress/compress.go b/backend/compress/compress.go
index 9656fb573..6d3397dfb 100644
--- a/backend/compress/compress.go
+++ b/backend/compress/compress.go
@@ -53,7 +53,7 @@ const (
 	Gzip         = 2
 )
 
-var nameRegexp = regexp.MustCompile("^(.+?)\\.([A-Za-z0-9-_]{11})$")
+var nameRegexp = regexp.MustCompile(`^(.+?)\.([A-Za-z0-9-_]{11})$`)
 
 // Register with Fs
 func init() {
diff --git a/backend/drive/drive.go b/backend/drive/drive.go
index 9aeeec5a0..c5f5827f7 100644
--- a/backend/drive/drive.go
+++ b/backend/drive/drive.go
@@ -2020,7 +2020,7 @@ func splitID(compositeID string) (actualID, shortcutID string) {
 
 // isShortcutID returns true if compositeID refers to a shortcut
 func isShortcutID(compositeID string) bool {
-	return strings.IndexRune(compositeID, shortcutSeparator) >= 0
+	return strings.ContainsRune(compositeID, shortcutSeparator)
 }
 
 // actualID returns an actual ID from a composite ID
@@ -3327,7 +3327,7 @@ func (f *Fs) Command(ctx context.Context, name string, arg []string, opt map[str
 			out["service_account_file"] = f.opt.ServiceAccountFile
 		}
 		if _, ok := opt["chunk_size"]; ok {
-			out["chunk_size"] = fmt.Sprintf("%s", f.opt.ChunkSize)
+			out["chunk_size"] = f.opt.ChunkSize.String()
 		}
 		return out, nil
 	case "set":
@@ -3344,11 +3344,11 @@ func (f *Fs) Command(ctx context.Context, name string, arg []string, opt map[str
 		}
 		if chunkSize, ok := opt["chunk_size"]; ok {
 			chunkSizeMap := make(map[string]string)
-			chunkSizeMap["previous"] = fmt.Sprintf("%s", f.opt.ChunkSize)
+			chunkSizeMap["previous"] = f.opt.ChunkSize.String()
 			if err = f.changeChunkSize(chunkSize); err != nil {
 				return out, err
 			}
-			chunkSizeString := fmt.Sprintf("%s", f.opt.ChunkSize)
+			chunkSizeString := f.opt.ChunkSize.String()
 			f.m.Set("chunk_size", chunkSizeString)
 			chunkSizeMap["current"] = chunkSizeString
 			out["chunk_size"] = chunkSizeMap
@@ -3392,13 +3392,13 @@ func (f *Fs) Command(ctx context.Context, name string, arg []string, opt map[str
 				names[name] = struct{}{}
 				lines = append(lines, "")
 				lines = append(lines, fmt.Sprintf("[%s]", name))
-				lines = append(lines, fmt.Sprintf("type = alias"))
+				lines = append(lines, "type = alias")
 				lines = append(lines, fmt.Sprintf("remote = %s,team_drive=%s,root_folder_id=:", f.name, drive.Id))
 				upstreams = append(upstreams, fmt.Sprintf(`"%s=%s:"`, name, name))
 			}
 			lines = append(lines, "")
-			lines = append(lines, fmt.Sprintf("[AllDrives]"))
-			lines = append(lines, fmt.Sprintf("type = combine"))
+			lines = append(lines, "[AllDrives]")
+			lines = append(lines, "type = combine")
 			lines = append(lines, fmt.Sprintf("upstreams = %s", strings.Join(upstreams, " ")))
 			return lines, nil
 		}
diff --git a/backend/filefabric/filefabric.go b/backend/filefabric/filefabric.go
index 7f49579b4..a70e1f7ae 100644
--- a/backend/filefabric/filefabric.go
+++ b/backend/filefabric/filefabric.go
@@ -490,7 +490,7 @@ func NewFs(ctx context.Context, name, root string, m configmap.Mapper) (fs.Fs, e
 				// Root is a dir - cache its ID
 				f.dirCache.Put(f.root, info.ID)
 			}
-		} else {
+			//} else {
 			// Root is not found so a directory
 		}
 	}
diff --git a/backend/googlecloudstorage/googlecloudstorage.go b/backend/googlecloudstorage/googlecloudstorage.go
index 2f8a88562..e010dfd88 100644
--- a/backend/googlecloudstorage/googlecloudstorage.go
+++ b/backend/googlecloudstorage/googlecloudstorage.go
@@ -391,7 +391,7 @@ func (f *Fs) Root() string {
 // String converts this Fs to a string
 func (f *Fs) String() string {
 	if f.rootBucket == "" {
-		return fmt.Sprintf("GCS root")
+		return "GCS root"
 	}
 	if f.rootDirectory == "" {
 		return fmt.Sprintf("GCS bucket %s", f.rootBucket)
diff --git a/backend/hdfs/object.go b/backend/hdfs/object.go
index d7b94afbd..976bc7f02 100644
--- a/backend/hdfs/object.go
+++ b/backend/hdfs/object.go
@@ -115,7 +115,7 @@ func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, op
 		return err
 	}
 
-	info, err := o.fs.client.Stat(realpath)
+	_, err = o.fs.client.Stat(realpath)
 	if err == nil {
 		err = o.fs.client.Remove(realpath)
 		if err != nil {
@@ -147,7 +147,7 @@ func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, op
 		return err
 	}
 
-	info, err = o.fs.client.Stat(realpath)
+	info, err := o.fs.client.Stat(realpath)
 	if err != nil {
 		return err
 	}
diff --git a/backend/hubic/hubic.go b/backend/hubic/hubic.go
index c655c038e..5c284194e 100644
--- a/backend/hubic/hubic.go
+++ b/backend/hubic/hubic.go
@@ -138,7 +138,7 @@ func (f *Fs) getCredentials(ctx context.Context) (err error) {
 		return err
 	}
 	f.expires = expires
-	fs.Debugf(f, "Got swift credentials (expiry %v in %v)", f.expires, f.expires.Sub(time.Now()))
+	fs.Debugf(f, "Got swift credentials (expiry %v in %v)", f.expires, time.Until(f.expires))
 	return nil
 }
 
diff --git a/backend/jottacloud/jottacloud.go b/backend/jottacloud/jottacloud.go
index 12fe1d255..735b5181f 100644
--- a/backend/jottacloud/jottacloud.go
+++ b/backend/jottacloud/jottacloud.go
@@ -1252,10 +1252,7 @@ func (f *Fs) Put(ctx context.Context, in io.Reader, src fs.ObjectInfo, options .
 func (f *Fs) mkParentDir(ctx context.Context, dirPath string) error {
 	// defer log.Trace(dirPath, "")("")
 	// chop off trailing / if it exists
-	if strings.HasSuffix(dirPath, "/") {
-		dirPath = dirPath[:len(dirPath)-1]
-	}
-	parent := path.Dir(dirPath)
+	parent := path.Dir(strings.TrimSuffix(dirPath, "/"))
 	if parent == "." {
 		parent = ""
 	}
diff --git a/backend/local/about_windows.go b/backend/local/about_windows.go
index d8daed764..f4fba6195 100644
--- a/backend/local/about_windows.go
+++ b/backend/local/about_windows.go
@@ -17,8 +17,12 @@ var getFreeDiskSpace = syscall.NewLazyDLL("kernel32.dll").NewProc("GetDiskFreeSp
 // About gets quota information
 func (f *Fs) About(ctx context.Context) (*fs.Usage, error) {
 	var available, total, free int64
+	root, e := syscall.UTF16PtrFromString(f.root)
+	if e != nil {
+		return nil, fmt.Errorf("failed to read disk usage: %w", e)
+	}
 	_, _, e1 := getFreeDiskSpace.Call(
-		uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(f.root))),
+		uintptr(unsafe.Pointer(root)),
 		uintptr(unsafe.Pointer(&available)), // lpFreeBytesAvailable - for this user
 		uintptr(unsafe.Pointer(&total)),     // lpTotalNumberOfBytes
 		uintptr(unsafe.Pointer(&free)),      // lpTotalNumberOfFreeBytes
diff --git a/backend/mailru/mailru.go b/backend/mailru/mailru.go
index 033f1d16d..24693c67c 100644
--- a/backend/mailru/mailru.go
+++ b/backend/mailru/mailru.go
@@ -1723,7 +1723,7 @@ func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, op
 		return err
 	}
 
-	if bytes.Compare(fileHash, newHash) != 0 {
+	if !bytes.Equal(fileHash, newHash) {
 		if o.fs.opt.CheckHash {
 			return mrhash.ErrorInvalidHash
 		}
@@ -2262,7 +2262,7 @@ func (e *endHandler) handle(err error) error {
 	}
 
 	newHash := e.hasher.Sum(nil)
-	if bytes.Compare(o.mrHash, newHash) == 0 {
+	if bytes.Equal(o.mrHash, newHash) {
 		return io.EOF
 	}
 	if o.fs.opt.CheckHash {
diff --git a/backend/netstorage/netstorage.go b/backend/netstorage/netstorage.go
index cbb2b0488..313679e27 100755
--- a/backend/netstorage/netstorage.go
+++ b/backend/netstorage/netstorage.go
@@ -903,22 +903,20 @@ func (f *Fs) netStorageStatRequest(ctx context.Context, URL string, directory bo
 		files = statResp.Files
 		f.setStatCache(URL, files)
 	}
-	if files != nil {
-		// Multiple objects can be returned with the "slash=both" option,
-		// when file/symlink/directory has the same name
-		for i := range files {
-			if files[i].Type == "symlink" {
-				// Add .rclonelink suffix to allow local backend code to convert to a symlink.
-				files[i].Name += ".rclonelink"
-				fs.Infof(nil, "Converting a symlink to the rclonelink on the stat request %s", files[i].Name)
-			}
-			entrywanted := (directory && files[i].Type == "dir") ||
-				(!directory && files[i].Type != "dir")
-			if entrywanted {
-				filestamp := files[0]
-				files[0] = files[i]
-				files[i] = filestamp
-			}
+	// Multiple objects can be returned with the "slash=both" option,
+	// when file/symlink/directory has the same name
+	for i := range files {
+		if files[i].Type == "symlink" {
+			// Add .rclonelink suffix to allow local backend code to convert to a symlink.
+			files[i].Name += ".rclonelink"
+			fs.Infof(nil, "Converting a symlink to the rclonelink on the stat request %s", files[i].Name)
+		}
+		entrywanted := (directory && files[i].Type == "dir") ||
+			(!directory && files[i].Type != "dir")
+		if entrywanted {
+			filestamp := files[0]
+			files[0] = files[i]
+			files[i] = filestamp
 		}
 	}
 	return files, nil
diff --git a/backend/onedrive/api/types.go b/backend/onedrive/api/types.go
index 394403b0e..236c1c682 100644
--- a/backend/onedrive/api/types.go
+++ b/backend/onedrive/api/types.go
@@ -292,7 +292,7 @@ type AsyncOperationStatus struct {
 func (i *Item) GetID() string {
 	if i.IsRemote() && i.RemoteItem.ID != "" {
 		return i.RemoteItem.ParentReference.DriveID + "#" + i.RemoteItem.ID
-	} else if i.ParentReference != nil && strings.Index(i.ID, "#") == -1 {
+	} else if i.ParentReference != nil && !strings.Contains(i.ID, "#") {
 		return i.ParentReference.DriveID + "#" + i.ID
 	}
 	return i.ID
diff --git a/backend/onedrive/onedrive.go b/backend/onedrive/onedrive.go
index 132ed77e6..a077843ed 100644
--- a/backend/onedrive/onedrive.go
+++ b/backend/onedrive/onedrive.go
@@ -660,7 +660,7 @@ func shouldRetry(ctx context.Context, resp *http.Response, err error) (bool, err
 				}
 			}
 		case 401:
-			if len(resp.Header["Www-Authenticate"]) == 1 && strings.Index(resp.Header["Www-Authenticate"][0], "expired_token") >= 0 {
+			if len(resp.Header["Www-Authenticate"]) == 1 && strings.Contains(resp.Header["Www-Authenticate"][0], "expired_token") {
 				retry = true
 				fs.Debugf(nil, "Should retry: %v", err)
 			} else if err != nil && strings.Contains(err.Error(), "Unable to initialize RPS") {
@@ -716,8 +716,7 @@ func (f *Fs) readMetaDataForPath(ctx context.Context, path string) (info *api.It
 	firstSlashIndex := strings.IndexRune(path, '/')
 
 	if f.driveType != driveTypePersonal || firstSlashIndex == -1 {
-		var opts rest.Opts
-		opts = f.newOptsCallWithPath(ctx, path, "GET", "")
+		opts := f.newOptsCallWithPath(ctx, path, "GET", "")
 		opts.Path = strings.TrimSuffix(opts.Path, ":")
 		err = f.pacer.Call(func() (bool, error) {
 			resp, err = f.srv.CallJSON(ctx, &opts, nil, &info)
@@ -1287,7 +1286,7 @@ func (f *Fs) Copy(ctx context.Context, src fs.Object, remote string) (fs.Object,
 	if srcObj.fs == f {
 		srcPath := srcObj.rootPath()
 		dstPath := f.rootPath(remote)
-		if strings.ToLower(srcPath) == strings.ToLower(dstPath) {
+		if strings.EqualFold(srcPath, dstPath) {
 			return nil, fmt.Errorf("can't copy %q -> %q as are same name when lowercase", srcPath, dstPath)
 		}
 	}
@@ -2185,7 +2184,7 @@ func (o *Object) ID() string {
 // Such a normalized ID can come from (*Item).GetID()
 func (f *Fs) parseNormalizedID(ID string) (string, string, string) {
 	rootURL := graphAPIEndpoint[f.opt.Region] + "/v1.0/drives"
-	if strings.Index(ID, "#") >= 0 {
+	if strings.Contains(ID, "#") {
 		s := strings.Split(ID, "#")
 		return s[1], s[0], rootURL
 	}
@@ -2359,6 +2358,9 @@ func (f *Fs) ChangeNotify(ctx context.Context, notifyFunc func(string, fs.EntryT
 
 func (f *Fs) changeNotifyStartPageToken(ctx context.Context) (nextDeltaToken string, err error) {
 	delta, err := f.changeNotifyNextChange(ctx, "latest")
+	if err != nil {
+		return
+	}
 	parsedURL, err := url.Parse(delta.DeltaLink)
 	if err != nil {
 		return
@@ -2388,6 +2390,9 @@ func (f *Fs) buildDriveDeltaOpts(token string) rest.Opts {
 
 func (f *Fs) changeNotifyRunner(ctx context.Context, notifyFunc func(string, fs.EntryType), deltaToken string) (nextDeltaToken string, err error) {
 	delta, err := f.changeNotifyNextChange(ctx, deltaToken)
+	if err != nil {
+		return
+	}
 	parsedURL, err := url.Parse(delta.DeltaLink)
 	if err != nil {
 		return
diff --git a/backend/opendrive/opendrive.go b/backend/opendrive/opendrive.go
index 96f29aa33..6e5967e62 100644
--- a/backend/opendrive/opendrive.go
+++ b/backend/opendrive/opendrive.go
@@ -361,7 +361,7 @@ func (f *Fs) Copy(ctx context.Context, src fs.Object, remote string) (fs.Object,
 
 	srcPath := srcObj.fs.rootSlash() + srcObj.remote
 	dstPath := f.rootSlash() + remote
-	if strings.ToLower(srcPath) == strings.ToLower(dstPath) {
+	if strings.EqualFold(srcPath, dstPath) {
 		return nil, fmt.Errorf("Can't copy %q -> %q as are same name when lowercase", srcPath, dstPath)
 	}
 
@@ -588,9 +588,6 @@ func (f *Fs) readMetaDataForFolderID(ctx context.Context, id string) (info *Fold
 	if err != nil {
 		return nil, err
 	}
-	if resp != nil {
-	}
-
 	return info, err
 }
 
@@ -611,13 +608,13 @@ func (f *Fs) Put(ctx context.Context, in io.Reader, src fs.ObjectInfo, options .
 		return nil, err
 	}
 
-	if "" == o.id {
+	if o.id == "" {
 		// Attempt to read ID, ignore error
 		// FIXME is this correct?
 		_ = o.readMetaData(ctx)
 	}
 
-	if "" == o.id {
+	if o.id == "" {
 		// We need to create an ID for this file
 		var resp *http.Response
 		response := createFileResponse{}
diff --git a/backend/pcloud/api/types.go b/backend/pcloud/api/types.go
index 974d9f5f3..5ae2b6b6b 100644
--- a/backend/pcloud/api/types.go
+++ b/backend/pcloud/api/types.go
@@ -136,7 +136,7 @@ func (g *GetFileLinkResult) IsValid() bool {
 	if len(g.Hosts) == 0 {
 		return false
 	}
-	return time.Time(g.Expires).Sub(time.Now()) > 30*time.Second
+	return time.Until(time.Time(g.Expires)) > 30*time.Second
 }
 
 // URL returns a URL from the Path and Hosts.  Check with IsValid
diff --git a/backend/pcloud/pcloud.go b/backend/pcloud/pcloud.go
index cb470599c..a73593b40 100644
--- a/backend/pcloud/pcloud.go
+++ b/backend/pcloud/pcloud.go
@@ -227,7 +227,7 @@ func shouldRetry(ctx context.Context, resp *http.Response, err error) (bool, err
 		}
 	}
 
-	if resp != nil && resp.StatusCode == 401 && len(resp.Header["Www-Authenticate"]) == 1 && strings.Index(resp.Header["Www-Authenticate"][0], "expired_token") >= 0 {
+	if resp != nil && resp.StatusCode == 401 && len(resp.Header["Www-Authenticate"]) == 1 && strings.Contains(resp.Header["Www-Authenticate"][0], "expired_token") {
 		doRetry = true
 		fs.Debugf(nil, "Should retry: %v", err)
 	}
diff --git a/backend/qingstor/qingstor.go b/backend/qingstor/qingstor.go
index d4053e250..5f5134099 100644
--- a/backend/qingstor/qingstor.go
+++ b/backend/qingstor/qingstor.go
@@ -573,9 +573,7 @@ func (f *Fs) list(ctx context.Context, bucket, directory, prefix string, addBuck
 				if addBucket {
 					remote = path.Join(bucket, remote)
 				}
-				if strings.HasSuffix(remote, "/") {
-					remote = remote[:len(remote)-1]
-				}
+				remote = strings.TrimSuffix(remote, "/")
 				err = fn(remote, &qs.KeyType{Key: &remote}, true)
 				if err != nil {
 					return err
@@ -775,8 +773,6 @@ func (f *Fs) makeBucket(ctx context.Context, bucket string) error {
 				retries++
 				wasDeleted = true
 				continue
-			default:
-				break
 			}
 			break
 		}
@@ -854,7 +850,6 @@ func (f *Fs) Rmdir(ctx context.Context, dir string) error {
 						continue
 					default:
 						err = e
-						break
 					}
 				}
 			} else {
diff --git a/backend/s3/s3.go b/backend/s3/s3.go
index ea04ed653..0a7b216f4 100644
--- a/backend/s3/s3.go
+++ b/backend/s3/s3.go
@@ -1976,7 +1976,7 @@ func (f *Fs) Root() string {
 // String converts this Fs to a string
 func (f *Fs) String() string {
 	if f.rootBucket == "" {
-		return fmt.Sprintf("S3 root")
+		return "S3 root"
 	}
 	if f.rootDirectory == "" {
 		return fmt.Sprintf("S3 bucket %s", f.rootBucket)
@@ -2669,9 +2669,7 @@ func (f *Fs) list(ctx context.Context, bucket, directory, prefix string, addBuck
 				if addBucket {
 					remote = path.Join(bucket, remote)
 				}
-				if strings.HasSuffix(remote, "/") {
-					remote = remote[:len(remote)-1]
-				}
+				remote = strings.TrimSuffix(remote, "/")
 				err = fn(remote, &s3.Object{Key: &remote}, true)
 				if err != nil {
 					return err
diff --git a/backend/seafile/seafile.go b/backend/seafile/seafile.go
index 76f9843ab..15a708641 100644
--- a/backend/seafile/seafile.go
+++ b/backend/seafile/seafile.go
@@ -453,7 +453,7 @@ func (f *Fs) Root() string {
 // String converts this Fs to a string
 func (f *Fs) String() string {
 	if f.libraryName == "" {
-		return fmt.Sprintf("seafile root")
+		return "seafile root"
 	}
 	library := "library"
 	if f.encrypted {
@@ -984,9 +984,9 @@ func (f *Fs) PublicLink(ctx context.Context, remote string, expire fs.Duration,
 	if err != nil {
 		return "", err
 	}
-	if shareLinks != nil && len(shareLinks) > 0 {
+	if len(shareLinks) > 0 {
 		for _, shareLink := range shareLinks {
-			if shareLink.IsExpired == false {
+			if !shareLink.IsExpired {
 				return shareLink.Link, nil
 			}
 		}
@@ -1053,7 +1053,7 @@ func (f *Fs) isLibraryInCache(libraryName string) bool {
 		return false
 	}
 	value, found := f.libraries.GetMaybe(librariesCacheKey)
-	if found == false {
+	if !found {
 		return false
 	}
 	libraries := value.([]api.Library)
@@ -1130,7 +1130,7 @@ func (f *Fs) mkLibrary(ctx context.Context, libraryName, password string) error
 	}
 	// Stores the library details into the cache
 	value, found := f.libraries.GetMaybe(librariesCacheKey)
-	if found == false {
+	if !found {
 		// Don't update the cache at that point
 		return nil
 	}
diff --git a/backend/sharefile/sharefile.go b/backend/sharefile/sharefile.go
index 69da61cb9..e6add47c2 100644
--- a/backend/sharefile/sharefile.go
+++ b/backend/sharefile/sharefile.go
@@ -1077,7 +1077,7 @@ func (f *Fs) Copy(ctx context.Context, src fs.Object, remote string) (dst fs.Obj
 	}
 	dstLeaf = f.opt.Enc.FromStandardName(dstLeaf)
 
-	sameName := strings.ToLower(srcLeaf) == strings.ToLower(dstLeaf)
+	sameName := strings.EqualFold(srcLeaf, dstLeaf)
 	if sameName && srcParentID == dstParentID {
 		return nil, fmt.Errorf("copy: can't copy to a file in the same directory whose name only differs in case: %q vs %q", srcLeaf, dstLeaf)
 	}
@@ -1096,7 +1096,7 @@ func (f *Fs) Copy(ctx context.Context, src fs.Object, remote string) (dst fs.Obj
 			directCopy = true
 		} else if err != nil {
 			return nil, fmt.Errorf("copy: failed to examine destination dir: %w", err)
-		} else {
+			//} else {
 			// otherwise need to copy via a temporary directory
 		}
 	}
diff --git a/backend/sia/sia.go b/backend/sia/sia.go
index 2017332ce..2a8235638 100644
--- a/backend/sia/sia.go
+++ b/backend/sia/sia.go
@@ -423,9 +423,7 @@ func NewFs(ctx context.Context, name, root string, m configmap.Mapper) (fs.Fs, e
 		return nil, err
 	}
 
-	if strings.HasSuffix(opt.APIURL, "/") {
-		opt.APIURL = strings.TrimSuffix(opt.APIURL, "/")
-	}
+	opt.APIURL = strings.TrimSuffix(opt.APIURL, "/")
 
 	// Parse the endpoint
 	u, err := url.Parse(opt.APIURL)
diff --git a/backend/sugarsync/sugarsync.go b/backend/sugarsync/sugarsync.go
index 4b57f4184..2041bdc05 100644
--- a/backend/sugarsync/sugarsync.go
+++ b/backend/sugarsync/sugarsync.go
@@ -872,7 +872,7 @@ func (f *Fs) Copy(ctx context.Context, src fs.Object, remote string) (fs.Object,
 
 	srcPath := srcObj.fs.rootSlash() + srcObj.remote
 	dstPath := f.rootSlash() + remote
-	if strings.ToLower(srcPath) == strings.ToLower(dstPath) {
+	if strings.EqualFold(srcPath, dstPath) {
 		return nil, fmt.Errorf("can't copy %q -> %q as are same name when lowercase", srcPath, dstPath)
 	}
 
diff --git a/backend/swift/swift.go b/backend/swift/swift.go
index 5e3d96933..074e2daf7 100644
--- a/backend/swift/swift.go
+++ b/backend/swift/swift.go
@@ -268,7 +268,7 @@ func (f *Fs) Root() string {
 // String converts this Fs to a string
 func (f *Fs) String() string {
 	if f.rootContainer == "" {
-		return fmt.Sprintf("Swift root")
+		return "Swift root"
 	}
 	if f.rootDirectory == "" {
 		return fmt.Sprintf("Swift container %s", f.rootContainer)
@@ -1271,7 +1271,7 @@ func (o *Object) getSegmentsLargeObject(ctx context.Context) (map[string][]strin
 		if _, ok := containerSegments[segmentContainer]; !ok {
 			containerSegments[segmentContainer] = make([]string, 0, len(segmentObjects))
 		}
-		segments, _ := containerSegments[segmentContainer]
+		segments := containerSegments[segmentContainer]
 		segments = append(segments, segment.Name)
 		containerSegments[segmentContainer] = segments
 	}
@@ -1363,7 +1363,7 @@ func (o *Object) updateChunks(ctx context.Context, in0 io.Reader, headers swift.
 			return
 		}
 		fs.Debugf(o, "Delete segments when err raise %v", err)
-		if segmentInfos == nil || len(segmentInfos) == 0 {
+		if len(segmentInfos) == 0 {
 			return
 		}
 		_ctx := context.Background()
@@ -1418,7 +1418,7 @@ func (o *Object) updateChunks(ctx context.Context, in0 io.Reader, headers swift.
 }
 
 func deleteChunks(ctx context.Context, o *Object, segmentsContainer string, segmentInfos []string) {
-	if segmentInfos == nil || len(segmentInfos) == 0 {
+	if len(segmentInfos) == 0 {
 		return
 	}
 	for _, v := range segmentInfos {
diff --git a/backend/uptobox/uptobox.go b/backend/uptobox/uptobox.go
index 5ffd4baf1..56c4f80a1 100644
--- a/backend/uptobox/uptobox.go
+++ b/backend/uptobox/uptobox.go
@@ -703,8 +703,7 @@ func (f *Fs) Move(ctx context.Context, src fs.Object, remote string) (fs.Object,
 	}
 
 	// copy the old object and apply the changes
-	var newObj Object
-	newObj = *srcObj
+	newObj := *srcObj
 	newObj.remote = remote
 	newObj.fs = f
 	return &newObj, nil
@@ -759,7 +758,7 @@ func (f *Fs) DirMove(ctx context.Context, src fs.Fs, srcRemote, dstRemote string
 	}
 	// check if the destination allready exists
 	dstPath := f.dirPath(dstRemote)
-	dstInfo, err := f.readMetaDataForPath(ctx, dstPath, &api.MetadataRequestOptions{Limit: 1})
+	_, err = f.readMetaDataForPath(ctx, dstPath, &api.MetadataRequestOptions{Limit: 1})
 	if err == nil {
 		return fs.ErrorDirExists
 	}
@@ -772,7 +771,7 @@ func (f *Fs) DirMove(ctx context.Context, src fs.Fs, srcRemote, dstRemote string
 	}
 
 	// find the destination parent dir
-	dstInfo, err = f.readMetaDataForPath(ctx, dstBase, &api.MetadataRequestOptions{Limit: 1})
+	dstInfo, err := f.readMetaDataForPath(ctx, dstBase, &api.MetadataRequestOptions{Limit: 1})
 	if err != nil {
 		return fmt.Errorf("dirmove: failed to read destination: %w", err)
 	}
diff --git a/backend/webdav/webdav.go b/backend/webdav/webdav.go
index 64c3e983a..7016b289c 100644
--- a/backend/webdav/webdav.go
+++ b/backend/webdav/webdav.go
@@ -716,9 +716,7 @@ func (f *Fs) listAll(ctx context.Context, dir string, directoriesOnly bool, file
 			subPath = f.opt.Enc.ToStandardPath(subPath)
 		}
 		remote := path.Join(dir, subPath)
-		if strings.HasSuffix(remote, "/") {
-			remote = remote[:len(remote)-1]
-		}
+		remote = strings.TrimSuffix(remote, "/")
 
 		// the listing contains info about itself which we ignore
 		if remote == dir {
@@ -820,10 +818,7 @@ func (f *Fs) PutStream(ctx context.Context, in io.Reader, src fs.ObjectInfo, opt
 func (f *Fs) mkParentDir(ctx context.Context, dirPath string) (err error) {
 	// defer log.Trace(dirPath, "")("err=%v", &err)
 	// chop off trailing / if it exists
-	if strings.HasSuffix(dirPath, "/") {
-		dirPath = dirPath[:len(dirPath)-1]
-	}
-	parent := path.Dir(dirPath)
+	parent := path.Dir(strings.TrimSuffix(dirPath, "/"))
 	if parent == "." {
 		parent = ""
 	}
diff --git a/backend/yandex/api/types.go b/backend/yandex/api/types.go
index 978b1d00c..7e346b1ff 100644
--- a/backend/yandex/api/types.go
+++ b/backend/yandex/api/types.go
@@ -131,7 +131,7 @@ func (m *SortMode) String() string {
 
 // UnmarshalJSON sort mode
 func (m *SortMode) UnmarshalJSON(value []byte) error {
-	if value == nil || len(value) == 0 {
+	if len(value) == 0 {
 		m.mode = ""
 		return nil
 	}
diff --git a/backend/yandex/yandex.go b/backend/yandex/yandex.go
index 265802cb5..7eea7187f 100644
--- a/backend/yandex/yandex.go
+++ b/backend/yandex/yandex.go
@@ -468,7 +468,7 @@ func (f *Fs) CreateDir(ctx context.Context, path string) (err error) {
 	}
 
 	// If creating a directory with a : use (undocumented) disk: prefix
-	if strings.IndexRune(path, ':') >= 0 {
+	if strings.ContainsRune(path, ':') {
 		path = "disk:" + path
 	}
 	opts.Parameters.Set("path", f.opt.Enc.FromStandardPath(path))
@@ -506,10 +506,8 @@ func (f *Fs) mkDirs(ctx context.Context, path string) (err error) {
 				var mkdirpath = "/"                   //path separator /
 				for _, element := range dirs {
 					if element != "" {
-						mkdirpath += element + "/" //path separator /
-						if err = f.CreateDir(ctx, mkdirpath); err != nil {
-							// ignore errors while creating dirs
-						}
+						mkdirpath += element + "/"      //path separator /
+						_ = f.CreateDir(ctx, mkdirpath) // ignore errors while creating dirs
 					}
 				}
 			}
@@ -522,10 +520,7 @@ func (f *Fs) mkDirs(ctx context.Context, path string) (err error) {
 func (f *Fs) mkParentDirs(ctx context.Context, resPath string) error {
 	// defer log.Trace(dirPath, "")("")
 	// chop off trailing / if it exists
-	if strings.HasSuffix(resPath, "/") {
-		resPath = resPath[:len(resPath)-1]
-	}
-	parent := path.Dir(resPath)
+	parent := path.Dir(strings.TrimSuffix(resPath, "/"))
 	if parent == "." {
 		parent = ""
 	}
diff --git a/backend/zoho/zoho.go b/backend/zoho/zoho.go
index e0082cacd..6175be3c5 100644
--- a/backend/zoho/zoho.go
+++ b/backend/zoho/zoho.go
@@ -287,7 +287,7 @@ func shouldRetry(ctx context.Context, resp *http.Response, err error) (bool, err
 	}
 	authRetry := false
 
-	if resp != nil && resp.StatusCode == 401 && len(resp.Header["Www-Authenticate"]) == 1 && strings.Index(resp.Header["Www-Authenticate"][0], "expired_token") >= 0 {
+	if resp != nil && resp.StatusCode == 401 && len(resp.Header["Www-Authenticate"]) == 1 && strings.Contains(resp.Header["Www-Authenticate"][0], "expired_token") {
 		authRetry = true
 		fs.Debugf(nil, "Should retry: %v", err)
 	}
diff --git a/cmd/cmd.go b/cmd/cmd.go
index f2aea1fdf..82ae1ad83 100644
--- a/cmd/cmd.go
+++ b/cmd/cmd.go
@@ -273,7 +273,7 @@ func Run(Retry bool, showStats bool, cmd *cobra.Command, f func() error) {
 			break
 		}
 		if retryAfter := accounting.GlobalStats().RetryAfter(); !retryAfter.IsZero() {
-			d := retryAfter.Sub(time.Now())
+			d := time.Until(retryAfter)
 			if d > 0 {
 				fs.Logf(nil, "Received retry after error - sleeping until %s (%v)", retryAfter.Format(time.RFC3339Nano), d)
 				time.Sleep(d)
@@ -458,7 +458,7 @@ func initConfig() {
 		})
 	}
 
-	if m, _ := regexp.MatchString("^(bits|bytes)$", *dataRateUnit); m == false {
+	if m, _ := regexp.MatchString("^(bits|bytes)$", *dataRateUnit); !m {
 		fs.Errorf(nil, "Invalid unit passed to --stats-unit. Defaulting to bytes.")
 		ci.DataRateUnit = "bytes"
 	} else {
diff --git a/cmd/cryptdecode/cryptdecode.go b/cmd/cryptdecode/cryptdecode.go
index c8859ebce..bdfd3e221 100644
--- a/cmd/cryptdecode/cryptdecode.go
+++ b/cmd/cryptdecode/cryptdecode.go
@@ -75,7 +75,7 @@ func cryptDecode(cipher *crypt.Cipher, args []string) error {
 		}
 	}
 
-	fmt.Printf(output)
+	fmt.Print(output)
 
 	return nil
 }
@@ -89,7 +89,7 @@ func cryptEncode(cipher *crypt.Cipher, args []string) error {
 		output += fmt.Sprintln(fileName, "\t", encryptedFileName)
 	}
 
-	fmt.Printf(output)
+	fmt.Print(output)
 
 	return nil
 }
diff --git a/cmd/selfupdate/selfupdate.go b/cmd/selfupdate/selfupdate.go
index f15bce83c..5bfd1cb66 100644
--- a/cmd/selfupdate/selfupdate.go
+++ b/cmd/selfupdate/selfupdate.go
@@ -200,9 +200,7 @@ func InstallUpdate(ctx context.Context, opt *Options) error {
 	savedFile := ""
 	if runtime.GOOS == "windows" {
 		savedFile = targetFile
-		if strings.HasSuffix(savedFile, ".exe") {
-			savedFile = savedFile[:len(savedFile)-4]
-		}
+		savedFile = strings.TrimSuffix(savedFile, ".exe")
 		savedFile += ".old.exe"
 	}
 
@@ -319,9 +317,7 @@ func makeRandomExeName(baseName, extension string) (string, error) {
 	const maxAttempts = 5
 
 	if runtime.GOOS == "windows" {
-		if strings.HasSuffix(baseName, ".exe") {
-			baseName = baseName[:len(baseName)-4]
-		}
+		baseName = strings.TrimSuffix(baseName, ".exe")
 		extension += ".exe"
 	}
 
diff --git a/cmd/serve/docker/docker.go b/cmd/serve/docker/docker.go
index f9f6fcd93..b9702d87f 100644
--- a/cmd/serve/docker/docker.go
+++ b/cmd/serve/docker/docker.go
@@ -20,7 +20,7 @@ var (
 	pluginName  = "rclone"
 	pluginScope = "local"
 	baseDir     = "/var/lib/docker-volumes/rclone"
-	sockDir     = "/run/docker/plugins"
+	sockDir     = "/run/docker/plugins" // location of unix sockets (only relevant on Linux and FreeBSD)
 	defSpecDir  = "/etc/docker/plugins"
 	stateFile   = "docker-plugin.state"
 	socketAddr  = "" // TCP listening address or empty string for Unix socket
diff --git a/cmd/serve/proxy/proxy.go b/cmd/serve/proxy/proxy.go
index 861861ee9..9e1d15878 100644
--- a/cmd/serve/proxy/proxy.go
+++ b/cmd/serve/proxy/proxy.go
@@ -156,11 +156,11 @@ func (p *Proxy) run(in map[string]string) (config configmap.Simple, err error) {
 	fs.Debugf(nil, "Calling proxy %v", p.cmdLine)
 	duration := time.Since(start)
 	if err != nil {
-		return nil, fmt.Errorf("proxy: failed on %v: %q: %w", p.cmdLine, strings.TrimSpace(string(stderr.Bytes())), err)
+		return nil, fmt.Errorf("proxy: failed on %v: %q: %w", p.cmdLine, strings.TrimSpace(stderr.String()), err)
 	}
 	err = json.Unmarshal(stdout.Bytes(), &config)
 	if err != nil {
-		return nil, fmt.Errorf("proxy: failed to read output: %q: %w", string(stdout.Bytes()), err)
+		return nil, fmt.Errorf("proxy: failed to read output: %q: %w", stdout.String(), err)
 	}
 	fs.Debugf(nil, "Proxy returned in %v", duration)
 
diff --git a/fs/accounting/accounting.go b/fs/accounting/accounting.go
index e3ed4972c..53d6549fb 100644
--- a/fs/accounting/accounting.go
+++ b/fs/accounting/accounting.go
@@ -444,7 +444,7 @@ func (acc *Account) speed() (bps, current float64) {
 		return 0, 0
 	}
 	// Calculate speed from first read.
-	total := float64(time.Now().Sub(acc.values.start)) / float64(time.Second)
+	total := float64(time.Since(acc.values.start)) / float64(time.Second)
 	if total > 0 {
 		bps = float64(acc.values.bytes) / total
 	} else {
diff --git a/fs/asyncreader/asyncreader.go b/fs/asyncreader/asyncreader.go
index 44f8fb245..a39ae8807 100644
--- a/fs/asyncreader/asyncreader.go
+++ b/fs/asyncreader/asyncreader.go
@@ -68,8 +68,8 @@ func (a *AsyncReader) init(rd io.ReadCloser, buffers int) {
 	a.in = rd
 	a.ready = make(chan *buffer, buffers)
 	a.token = make(chan struct{}, buffers)
-	a.exit = make(chan struct{}, 0)
-	a.exited = make(chan struct{}, 0)
+	a.exit = make(chan struct{})
+	a.exited = make(chan struct{})
 	a.buffers = buffers
 	a.cur = nil
 	a.size = softStartInitial
diff --git a/fs/config/configfile/configfile.go b/fs/config/configfile/configfile.go
index 65971492d..df01f25a5 100644
--- a/fs/config/configfile/configfile.go
+++ b/fs/config/configfile/configfile.go
@@ -187,10 +187,7 @@ func (s *Storage) Serialize() (string, error) {
 func (s *Storage) HasSection(section string) bool {
 	s.check()
 	_, err := s.gc.GetSection(section)
-	if err != nil {
-		return false
-	}
-	return true
+	return err == nil
 }
 
 // DeleteSection removes the named section and all config from the
diff --git a/fs/config/rc.go b/fs/config/rc.go
index fbbc7bdc9..db5d48cca 100644
--- a/fs/config/rc.go
+++ b/fs/config/rc.go
@@ -72,10 +72,7 @@ See the [listremotes command](/commands/rclone_listremotes/) command for more in
 
 // Return the a list of remotes in the config file
 func rcListRemotes(ctx context.Context, in rc.Params) (out rc.Params, err error) {
-	var remotes = []string{}
-	for _, remote := range LoadedData().GetSectionList() {
-		remotes = append(remotes, remote)
-	}
+	remotes := LoadedData().GetSectionList()
 	out = rc.Params{
 		"remotes": remotes,
 	}
diff --git a/fs/filter/filter.go b/fs/filter/filter.go
index 37071f9d2..e35018fa5 100644
--- a/fs/filter/filter.go
+++ b/fs/filter/filter.go
@@ -596,7 +596,7 @@ func (f *Filter) UsesDirectoryFilters() bool {
 	}
 	rule := f.dirRules.rules[0]
 	re := rule.Regexp.String()
-	if rule.Include == true && re == "^.*$" {
+	if rule.Include && re == "^.*$" {
 		return false
 	}
 	return true
diff --git a/fs/fserrors/error.go b/fs/fserrors/error.go
index 4cdf7ac9d..12c971cd2 100644
--- a/fs/fserrors/error.go
+++ b/fs/fserrors/error.go
@@ -258,7 +258,7 @@ func NewErrorRetryAfter(d time.Duration) ErrorRetryAfter {
 
 // Error returns the textual version of the error
 func (e ErrorRetryAfter) Error() string {
-	return fmt.Sprintf("try again after %v (%v)", time.Time(e).Format(time.RFC3339Nano), time.Time(e).Sub(time.Now()))
+	return fmt.Sprintf("try again after %v (%v)", time.Time(e).Format(time.RFC3339Nano), time.Until(time.Time(e)))
 }
 
 // RetryAfter returns the time the operation should be retried at or
diff --git a/fs/fspath/path.go b/fs/fspath/path.go
index 99bdbe28b..285d843ff 100644
--- a/fs/fspath/path.go
+++ b/fs/fspath/path.go
@@ -99,7 +99,7 @@ func Parse(path string) (parsed Parsed, err error) {
 		return parsed, errCantBeEmpty
 	}
 	// If path has no `:` in, it must be a local path
-	if strings.IndexRune(path, ':') < 0 {
+	if !strings.ContainsRune(path, ':') {
 		return parsed, nil
 	}
 	// States for parser
diff --git a/fs/log/caller_hook.go b/fs/log/caller_hook.go
index 7a72999e4..13941bc02 100644
--- a/fs/log/caller_hook.go
+++ b/fs/log/caller_hook.go
@@ -45,7 +45,7 @@ func findCaller(skip int) string {
 	line := 0
 	for i := 0; i < 10; i++ {
 		file, line = getCaller(skip + i)
-		if !strings.HasPrefix(file, "logrus") && strings.Index(file, "log.go") < 0 {
+		if !strings.HasPrefix(file, "logrus") && !strings.Contains(file, "log.go") {
 			break
 		}
 	}
diff --git a/fs/open_options.go b/fs/open_options.go
index 84e43045c..938e38d04 100644
--- a/fs/open_options.go
+++ b/fs/open_options.go
@@ -75,7 +75,7 @@ func ParseRangeOption(s string) (po *RangeOption, err error) {
 		return nil, errors.New("Range: header invalid: doesn't start with " + preamble)
 	}
 	s = s[len(preamble):]
-	if strings.IndexRune(s, ',') >= 0 {
+	if strings.ContainsRune(s, ',') {
 		return nil, errors.New("Range: header invalid: contains multiple ranges which isn't supported")
 	}
 	dash := strings.IndexRune(s, '-')
@@ -250,7 +250,7 @@ func (o NullOption) Header() (key string, value string) {
 
 // String formats the option into human-readable form
 func (o NullOption) String() string {
-	return fmt.Sprintf("NullOption()")
+	return "NullOption()"
 }
 
 // Mandatory returns whether the option must be parsed or can be ignored
diff --git a/fs/rc/rcserver/rcserver.go b/fs/rc/rcserver/rcserver.go
index e2f8bd22e..0d246a2f6 100644
--- a/fs/rc/rcserver/rcserver.go
+++ b/fs/rc/rcserver/rcserver.go
@@ -378,7 +378,7 @@ func (s *Server) handleGet(w http.ResponseWriter, r *http.Request, path string)
 		if s.opt.WebUI {
 			pluginsMatchResult := webgui.PluginsMatch.FindStringSubmatch(path)
 
-			if pluginsMatchResult != nil && len(pluginsMatchResult) > 2 {
+			if len(pluginsMatchResult) > 2 {
 				ok := webgui.ServePluginOK(w, r, pluginsMatchResult)
 				if !ok {
 					r.URL.Path = fmt.Sprintf("/%s/%s/app/build/%s", pluginsMatchResult[1], pluginsMatchResult[2], pluginsMatchResult[3])
diff --git a/fs/rc/webgui/plugins.go b/fs/rc/webgui/plugins.go
index 3a97fd679..8f212be58 100644
--- a/fs/rc/webgui/plugins.go
+++ b/fs/rc/webgui/plugins.go
@@ -293,7 +293,7 @@ func ServePluginOK(w http.ResponseWriter, r *http.Request, pluginsMatchResult []
 	return true
 }
 
-var referrerPathReg = regexp.MustCompile("^(https?):\\/\\/(.+):([0-9]+)?\\/(.*)\\/?\\?(.*)$")
+var referrerPathReg = regexp.MustCompile(`^(https?):\/\/(.+):([0-9]+)?\/(.*)\/?\?(.*)$`)
 
 // ServePluginWithReferrerOK check if redirectReferrer is set for the referred a plugin, if yes,
 // sends a redirect to actual url. This function is useful for plugins to refer to absolute paths when
@@ -306,9 +306,9 @@ func ServePluginWithReferrerOK(w http.ResponseWriter, r *http.Request, path stri
 	referrer := r.Referer()
 	referrerPathMatch := referrerPathReg.FindStringSubmatch(referrer)
 
-	if referrerPathMatch != nil && len(referrerPathMatch) > 3 {
+	if len(referrerPathMatch) > 3 {
 		referrerPluginMatch := PluginsMatch.FindStringSubmatch(referrerPathMatch[4])
-		if referrerPluginMatch != nil && len(referrerPluginMatch) > 2 {
+		if len(referrerPluginMatch) > 2 {
 			pluginKey := fmt.Sprintf("%s/%s", referrerPluginMatch[1], referrerPluginMatch[2])
 			currentPlugin, err := loadedPlugins.GetPluginByName(pluginKey)
 			if err != nil {
diff --git a/lib/encoder/internal/gen/main.go b/lib/encoder/internal/gen/main.go
index 38d33e1ca..a94c37935 100644
--- a/lib/encoder/internal/gen/main.go
+++ b/lib/encoder/internal/gen/main.go
@@ -465,17 +465,13 @@ func getMapping(mask encoder.MultiEncoder) mapping {
 }
 func collectEncodables(m []mapping) (out []rune) {
 	for _, s := range m {
-		for _, r := range s.src {
-			out = append(out, r)
-		}
+		out = append(out, s.src...)
 	}
 	return
 }
 func collectEncoded(m []mapping) (out []rune) {
 	for _, s := range m {
-		for _, r := range s.dst {
-			out = append(out, r)
-		}
+		out = append(out, s.dst...)
 	}
 	return
 }
diff --git a/lib/oauthutil/oauthutil.go b/lib/oauthutil/oauthutil.go
index 56c936d71..53a08900c 100644
--- a/lib/oauthutil/oauthutil.go
+++ b/lib/oauthutil/oauthutil.go
@@ -280,7 +280,7 @@ func (ts *TokenSource) timeToExpiry() time.Duration {
 	if t.Expiry.IsZero() {
 		return 3e9 * time.Second // ~95 years
 	}
-	return t.Expiry.Sub(time.Now())
+	return time.Until(t.Expiry)
 }
 
 // OnExpiry returns a channel which has the time written to it when
@@ -766,7 +766,6 @@ func (s *authServer) Init() error {
 		}
 		fs.Debugf(nil, "Redirecting browser to: %s", s.authURL)
 		http.Redirect(w, req, s.authURL, http.StatusTemporaryRedirect)
-		return
 	})
 	mux.HandleFunc("/", s.handleAuth)
 
diff --git a/lib/pacer/tokens.go b/lib/pacer/tokens.go
index b4f905ba9..d1e1e7cea 100644
--- a/lib/pacer/tokens.go
+++ b/lib/pacer/tokens.go
@@ -22,7 +22,6 @@ func NewTokenDispenser(n int) *TokenDispenser {
 // Get gets a token from the pool - don't forget to return it with Put
 func (td *TokenDispenser) Get() {
 	<-td.tokens
-	return
 }
 
 // Put returns a token
diff --git a/lib/version/version.go b/lib/version/version.go
index 0aebe3d61..e8a4c17f6 100644
--- a/lib/version/version.go
+++ b/lib/version/version.go
@@ -11,7 +11,7 @@ import (
 
 const versionFormat = "-v2006-01-02-150405.000"
 
-var versionRegexp = regexp.MustCompile("-v\\d{4}-\\d{2}-\\d{2}-\\d{6}-\\d{3}")
+var versionRegexp = regexp.MustCompile(`-v\d{4}-\d{2}-\d{2}-\d{6}-\d{3}`)
 
 // Split fileName into base and extension so that base + ext == fileName
 func splitExt(fileName string) (base, ext string) {
diff --git a/vfs/dir.go b/vfs/dir.go
index 2b2eda5a2..004595b77 100644
--- a/vfs/dir.go
+++ b/vfs/dir.go
@@ -506,7 +506,7 @@ func (d *Dir) _purgeVirtual() {
 				// if remote can have empty directories then a
 				// new dir will be read in the listing
 				d._deleteVirtual(name)
-			} else {
+				//} else {
 				// leave the empty directory marker
 			}
 		case vAddFile:
diff --git a/vfs/read.go b/vfs/read.go
index a128b0313..88bcabfce 100644
--- a/vfs/read.go
+++ b/vfs/read.go
@@ -477,7 +477,7 @@ func (fh *ReadFileHandle) Release() error {
 	err := fh.close()
 	if err != nil {
 		fs.Errorf(fh.remote, "ReadFileHandle.Release error: %v", err)
-	} else {
+		//} else {
 		// fs.Debugf(fh.remote, "ReadFileHandle.Release OK")
 	}
 	return err
diff --git a/vfs/vfscache/downloaders/downloaders.go b/vfs/vfscache/downloaders/downloaders.go
index c6d769a4f..42ca677fd 100644
--- a/vfs/vfscache/downloaders/downloaders.go
+++ b/vfs/vfscache/downloaders/downloaders.go
@@ -359,7 +359,7 @@ func (dls *Downloaders) _ensureDownloader(r ranges.Range) (err error) {
 		return nil
 	}
 	// Downloader not found so start a new one
-	dl, err = dls._newDownloader(r)
+	_, err = dls._newDownloader(r)
 	if err != nil {
 		dls._countErrors(0, err)
 		return fmt.Errorf("failed to start downloader: %w", err)
diff --git a/vfs/vfscache/item.go b/vfs/vfscache/item.go
index d61d6062a..d6a6e20b8 100644
--- a/vfs/vfscache/item.go
+++ b/vfs/vfscache/item.go
@@ -779,7 +779,7 @@ func (item *Item) _checkObject(o fs.Object) error {
 			} else {
 				fs.Debugf(item.name, "vfs cache: remote object has gone but local object modified - keeping it")
 			}
-		} else {
+			//} else {
 			// no remote object && no local object
 			// OK
 		}
@@ -946,7 +946,7 @@ func (item *Item) Reset() (rr ResetResult, spaceFreed int64, err error) {
 
 	/* Do not need to reset an empty cache file unless it was being reset and the reset failed.
 	   Some thread(s) may be waiting on the reset's succesful completion in that case. */
-	if item.info.Rs.Size() == 0 && item.beingReset == false {
+	if item.info.Rs.Size() == 0 && !item.beingReset {
 		return SkippedEmpty, 0, nil
 	}
 
diff --git a/vfs/write.go b/vfs/write.go
index 4f0590312..24595e041 100644
--- a/vfs/write.go
+++ b/vfs/write.go
@@ -247,7 +247,7 @@ func (fh *WriteFileHandle) Flush() error {
 	err := fh.close()
 	if err != nil {
 		fs.Errorf(fh.remote, "WriteFileHandle.Flush error: %v", err)
-	} else {
+		//} else {
 		// fs.Debugf(fh.remote, "WriteFileHandle.Flush OK")
 	}
 	return err
@@ -268,7 +268,7 @@ func (fh *WriteFileHandle) Release() error {
 	err := fh.close()
 	if err != nil {
 		fs.Errorf(fh.remote, "WriteFileHandle.Release error: %v", err)
-	} else {
+		//} else {
 		// fs.Debugf(fh.remote, "WriteFileHandle.Release OK")
 	}
 	return err


Test patch:
diff --git a/backend/googlephotos/googlephotos_test.go b/backend/googlephotos/googlephotos_test.go
index c65a02700..11cb28e1b 100644
--- a/backend/googlephotos/googlephotos_test.go
+++ b/backend/googlephotos/googlephotos_test.go
@@ -37,7 +37,7 @@ func TestIntegration(t *testing.T) {
 	}
 	f, err := fs.NewFs(ctx, *fstest.RemoteName)
 	if err == fs.ErrorNotFoundInConfigFile {
-		t.Skip(fmt.Sprintf("Couldn't create google photos backend - skipping tests: %v", err))
+		t.Skipf("Couldn't create google photos backend - skipping tests: %v", err)
 	}
 	require.NoError(t, err)
 
diff --git a/backend/googlephotos/pattern_test.go b/backend/googlephotos/pattern_test.go
index 6d58e5475..b446615c2 100644
--- a/backend/googlephotos/pattern_test.go
+++ b/backend/googlephotos/pattern_test.go
@@ -50,7 +50,7 @@ func (f *testLister) listAlbums(ctx context.Context, shared bool) (all *albums,
 
 // mock listUploads for testing
 func (f *testLister) listUploads(ctx context.Context, dir string) (entries fs.DirEntries, err error) {
-	entries, _ = f.uploaded[dir]
+	entries = f.uploaded[dir]
 	return entries, nil
 }
 
diff --git a/cmd/test/makefiles/makefiles.go b/cmd/test/makefiles/makefiles.go
index 83a1f5842..b778accd0 100644
--- a/cmd/test/makefiles/makefiles.go
+++ b/cmd/test/makefiles/makefiles.go
@@ -254,7 +254,6 @@ func (d *dir) createDirectories() {
 			return
 		}
 	}
-	return
 }
 
 // list the directory hierarchy
diff --git a/fs/accounting/stats_groups_test.go b/fs/accounting/stats_groups_test.go
index 2fd08c39d..c111a08d8 100644
--- a/fs/accounting/stats_groups_test.go
+++ b/fs/accounting/stats_groups_test.go
@@ -111,9 +111,9 @@ func TestStatsGroupOperations(t *testing.T) {
 		runtime.GC()
 		runtime.ReadMemStats(&end)
 
-		t.Log(fmt.Sprintf("%+v\n%+v", start, end))
+		t.Logf("%+v\n%+v", start, end)
 		diff := percentDiff(start.HeapObjects, end.HeapObjects)
-		if diff > 1 || diff < 0 {
+		if diff > 1 {
 			t.Errorf("HeapObjects = %d, expected %d", end.HeapObjects, start.HeapObjects)
 		}
 	})
diff --git a/fs/fserrors/error_test.go b/fs/fserrors/error_test.go
index a0cf4e2e6..ec8426660 100644
--- a/fs/fserrors/error_test.go
+++ b/fs/fserrors/error_test.go
@@ -181,7 +181,7 @@ func TestShouldRetry(t *testing.T) {
 func TestRetryAfter(t *testing.T) {
 	e := NewErrorRetryAfter(time.Second)
 	after := e.RetryAfter()
-	dt := after.Sub(time.Now())
+	dt := time.Until(after)
 	assert.True(t, dt >= 900*time.Millisecond && dt <= 1100*time.Millisecond)
 	assert.True(t, IsRetryAfterError(e))
 	assert.False(t, IsRetryAfterError(io.EOF))
diff --git a/fstest/fstests/fstests.go b/fstest/fstests/fstests.go
index 11cf4dc8b..58481416a 100644
--- a/fstest/fstests/fstests.go
+++ b/fstest/fstests/fstests.go
@@ -387,8 +387,7 @@ func Run(t *testing.T, opt *Opt) {
 	// Skip if remote is not SetTier and GetTier capable
 	skipIfNotSetTier := func(t *testing.T) {
 		skipIfNotOk(t)
-		if f.Features().SetTier == false ||
-			f.Features().GetTier == false {
+		if !f.Features().SetTier || !f.Features().GetTier {
 			t.Skip("FS has no SetTier & GetTier interfaces")
 		}
 	}
@@ -735,7 +734,7 @@ func Run(t *testing.T, opt *Opt) {
 
 			TestPutLarge(ctx, t, f, &fstest.Item{
 				ModTime: fstest.Time("2001-02-03T04:05:06.499999999Z"),
-				Path:    fmt.Sprintf("zero-length-file"),
+				Path:    "zero-length-file",
 				Size:    int64(0),
 			})
 		})
diff --git a/lib/kv/internal_test.go b/lib/kv/internal_test.go
index ae40c6ed4..1c0834fbb 100644
--- a/lib/kv/internal_test.go
+++ b/lib/kv/internal_test.go
@@ -17,7 +17,6 @@ func TestKvConcurrency(t *testing.T) {
 	require.Equal(t, 0, len(dbMap), "no databases can be started initially")
 
 	const threadNum = 5
-	const facility = "test"
 	var wg sync.WaitGroup
 	ctx := context.Background()
 	results := make([]*DB, threadNum)
@@ -55,7 +54,6 @@ func TestKvConcurrency(t *testing.T) {
 func TestKvExit(t *testing.T) {
 	require.Equal(t, 0, len(dbMap), "no databases can be started initially")
 	const dbNum = 5
-	const openNum = 2
 	ctx := context.Background()
 	for i := 0; i < dbNum; i++ {
 		facility := fmt.Sprintf("test-%d", i)
diff --git a/vfs/vfstest/write_non_unix.go b/vfs/vfstest/write_non_unix.go
index 84b5bf00a..637d5b5bd 100644
--- a/vfs/vfstest/write_non_unix.go
+++ b/vfs/vfstest/write_non_unix.go
@@ -17,10 +17,7 @@ func TestWriteFileDoubleClose(t *testing.T) {
 
 // writeTestDup performs the platform-specific implementation of the dup() syscall
 func writeTestDup(oldfd uintptr) (uintptr, error) {
-	p, err := windows.GetCurrentProcess()
-	if err != nil {
-		return 0, err
-	}
+	p := windows.CurrentProcess()
 	var h windows.Handle
 	return uintptr(h), windows.DuplicateHandle(p, windows.Handle(oldfd), p, &h, 0, true, windows.DUPLICATE_SAME_ACCESS)
 }